Django Rest Framework 避免 JWT 认证

9

我正在使用rest_framework_simplejwt对我的用户进行身份验证,但在某些视图中,我需要忽略它,因为这些是公共视图。我想要在视图流程中检查令牌。期望的行为是:

在公共视图中

  • 避免令牌验证:如果有过期或无效的令牌,请忽略它,并让我在APIView中验证它

实际上,rest_framework_simplejwt会检查令牌并在令牌无效或过期时引发401错误...

我尝试禁用APIView内的authentication_classes,如下所示:

class SpecificProductApi(APIView):

    def get_authenticators(self):
        if self.request.method == 'GET':
            self.authentication_classes = []
        return super(SpecificProductApi, self).get_authenticators()

但是如果我在进入GET APIView方法之前禁用它,我就无法执行if reques.user.is_authenticated:因为我已经禁用了令牌:(

存在一种方式可以使用户手动进入视图并进行检查吗?谢谢


我不明白你在使用 request.user.is_authenticated 时遇到了什么问题。你能解释一下吗? - JPG
你好!感谢回答,如果我使用 self.authentication_classes = [],那么在视图中使用 request.user.is_authenticated 就没有效果,它总是返回 false。我需要在视图内手动处理身份验证,避免在外部进行验证。希望我表达得更清楚了,非常感谢 :) - Jhon Sanz
我想知道处理这个的标准方法是什么,就像一个规范。有人知道吗? - Darwin
4个回答

9
我通过添加 authentication_classes = [] 来完成它。
from rest_framework import permissions

class SpecificProductApi(APIView):
    permission_classes = [permissions.AllowAny]
    authentication_classes = []

1
简单的解决方案 - Lee Loftiss

9

您可以在视图中简单地使用 authentication_classes = [],但这总是会绕过JWT身份验证,即使有一个有效的包含令牌的Authorization标头也是如此。最好按如下方式扩展JWTAuthentication类(类似于Jhon Edwin Sanz Gonzalez的注释):

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken


class JWTAuthenticationSafe(JWTAuthentication):
    def authenticate(self, request):
        try:
            return super().authenticate(request=request)
        except InvalidToken:
            return None

然后在你的视图函数中使用authentication_classes = [JWTAuthenticationSafe]


这是最好的答案。 - Cameron Sima
这解决了我的问题。 - csassis

1
我有一个非常类似的问题。要创建公共端点,您必须覆盖验证器,否则将在令牌过期/丢失时返回401/403。
但是,公共端点并不意味着它不应该进行身份验证。相反,它应该有一个用于无身份验证/过期身份验证的响应,以及另一个用于有效身份验证的响应。
我不知道这是否是“正确”的方法,但这是我面对同样的问题想出来的解决方案。
像您所做的那样覆盖验证器,并在视图中添加一个额外的验证验证器的方法。
例如:
class SomeApiView(APIView):
    def get_authenticators(self):
        # Override standard view authenticators.
        # Public endpoint, no auth is enforced.
        return []

    def get_auth(self):
        # Return a generator of all authenticators.
        return (auth() for auth in self.authentication_classes)

    def get_auth_detail(self, request):
        # Evaluate each authenticator and return the first valid authentication.
        for auth in self.get_auth():
            # You probably need try / except here to catch authenticators 
            # that are invalid (403/401) in the case of multiple authentication 
            # classes--such as token auth, session auth, etc...
            auth_detail = auth.authenticate(request)
            if auth_detail:
                return auth_detail

        return None, None

    def post(self, request):
        # Returns a tuple of (User, JWT), can be (None, None)
        user, jwt = self.get_auth_detail(request)  

        # Do your magic based on user value.
        if user:
            # User is authenticated.
        else:
            # User is anon / not-authenticated.

1
你好,非常感谢,这太棒了。我刚刚将 get_auth_detail 方法更改为try: auth_detail = auth.authenticate(request) except: auth_detail = None我不知道为什么 auth_detail = auth.authenticate(request) 会引发 Http403 异常,所以我使用了 try catch。我将继续寻找如何改进这个解决方案。非常感谢。 - Jhon Sanz
如果您定义了多个身份验证类,例如会话身份验证、令牌身份验证,则最有可能一个身份验证器将通过,而另一个身份验证器将失败403。这将取决于顺序。因此,可能需要评估所有身份验证器,直到它们全部失败或单个有效身份验证。即,尝试/除外。我认为我在我的项目中只使用了一个jwt身份验证器,所以我没有看到这个问题。 - James

0
你只需要为相关视图指定权限类即可。
from rest_framework.permissions import AllowAny

class SpecificProductApi(APIView):
    permission_classes = (AllowAny, )

这个权限允许任何人通过URL访问这个特定的视图。


在我的测试中,这并不起作用。过期的令牌会触发401响应;因此这是我的答案。 - James

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