退出Django Rest Framework JWT

19
我想问一下,在使用 JWT 时注销是否是个好主意。 为了登录,我会发送一个包含用户名和密码的 POST 请求,以获取所需的令牌(保存在 localStorage 中),该令牌将允许我向需要令牌的视图发送进一步的请求。
但我不确定如何注销用户。我可以清除 localStorage,但令牌仍然可用。
因此,我想问一下是否应该刷新令牌,因为我无法禁用它。
4个回答

18

你说得对,即使你删除JWT令牌,它在过期之前仍然是有效的令牌。JWT是无状态的。因此,如果您想处理注销并使令牌失效,则必须需要保留一个数据库或内存缓存来存储已失效(被列入黑名单)的令牌。然后,您需要添加新的权限来检查令牌是否在黑名单中。

class BlackListedToken(models.Model):
    token = models.CharField(max_length=500)
    user = models.ForeignKey(User, related_name="token_user", on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("token", "user")


class IsTokenValid(BasePermission):
    def has_permission(self, request, view):
        user_id = request.user.id            
        is_allowed_user = True
        token = request.auth.decode("utf-8")
        try:
            is_blackListed = BlackListedToken.objects.get(user=user_id, token=token)
            if is_blackListed:
                is_allowed_user = False
        except BlackListedToken.DoesNotExist:
            is_allowed_user = True
        return is_allowed_user
您可以在令牌过期后将其从黑名单列表中移除。

8
如果每次调用都必须调用数据库,那么DRF令牌认证和JWT不就是一样的吗? - Nikhil Bhardwaj
我们不需要将每个令牌存储在数据库/内存中。我们只需要保留有效的令牌直到它们过期即可。最好的方法是使用一些缓存机制,并通过设置TTL来自动使令牌过期。 - a_k_v

10
你无法在创建令牌后手动使其过期。因此,与会话不同,在服务器端使用JWT时,你实际上不能注销。JWT是无状态的,这意味着你应该将需要的所有内容存储在有效负载中,并跳过每个请求上的数据库查询。但是,如果你计划拥有严格的注销功能,不能等待令牌自动过期,即使已从客户端清除了令牌,那么你可能需要忽略无状态逻辑并执行一些查询。那么解决方案是什么呢?
- 在令牌上设置合理的过期时间 - 在注销时从客户端删除存储的令牌 - 在每个授权请求上查询提供的令牌是否在“黑名单”中
“黑名单”列出了所有不再有效且尚未过期的令牌。你可以使用具有文档TTL选项的DB,该选项将设置为令牌过期前剩余的时间量。
Redis
Redis是一个很好的选择用于黑名单,它将允许快速的内存访问列表。然后,在某种中间件中运行每个授权请求时,您应该检查提供的令牌是否在黑名单中。如果是,则应抛出未经授权的错误。如果不是,则让它继续,JWT验证将处理它并确定它是否已过期或仍然有效。
有关更多信息,请参见使用JWT时如何注销。作者:Arpy Vanyan

3
关于你提到的 Redis,它很棒。但是,Redis 是基于内存的,所以如果服务器重启,所有数据都将丢失,并且每个令牌都需要重新验证。 - Reza Torkaman Ahmadi
1
@RezaTorkamanAhmadi 如果我们在单独的集群中运行Redis会怎样呢?这样,它将被单独维护,然后我们就不需要担心服务器重启问题了。 - reeversedev
1
是的,那也是一个很好的选择。我自己是用 jwt 的自定义方法解决的,在负载中查找 ID 并进行数据库检查验证。但是这种 redis 方法可能会更快。但是项目应该规模较大,因为在小到中等规模的项目中,我猜它会给项目带来复杂性。 - Reza Torkaman Ahmadi

3
每个JWT都应该有一个过期时间,所以当您注销用户时,应该从本地存储cookie中删除jwt-token。
但是令牌仍然可用。
不确定上面的那行是什么意思,但是在您清除它从本地存储和cookie后,不必担心令牌是否仍然对用户可用,因为无论如何,在过期日期之后它都将无效。

11
“令牌仍然可用”的问题是,即使您从会话中注销,该令牌在一定时间内仍然有效(在到期之前)。 因此,拥有您的令牌的人可以成功验证并登录系统。 这是一个安全漏洞! - a_k_v

1

实现这个功能的更简单的方法是使用rest_framework_simplejwt包。我相信您也已经使用了同样的包来生成JWT。

当用户执行注销操作时,您需要从前端清除缓存,并将刷新令牌添加到后端的黑名单中。

访问令牌的生命周期很短,不需要列入黑名单,最好为访问令牌设置最小寿命。这样它们最终会过期。

rest_framework_simplejwt.token_blacklist默认只会列入刷新令牌的黑名单。

您只需要在settings.py INSTALLED_APPS中添加以下应用程序即可。

INSTALLED_APPS = (
    'rest_framework_simplejwt.token_blacklist',
)

还要为TokenBlacklistView配置urls.py

from rest_framework_simplejwt.views import TokenBlacklistView

    urlpatterns = [
      ...
      path('logout/', TokenBlacklistView.as_view(), name='token_blacklist'),
      ...
    ]

Source: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/blacklist_app.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接