You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何ViewSet权限未被覆盖?创建用户接口鉴权报错排查

问题:未授权访问用户创建接口时触发"No token found"错误

我创建了一个用于认证的ViewSet,希望它对未授权用户开放访问,按照DRF文档设置了permission_classes = [AllowAny]:

class AuthenticationViewSet(viewsets.ViewSet):
    permission_classes = [AllowAny]

    def create_user(self, request: Request) -> Response:
        # 用户创建逻辑代码

同时为了让其他ViewSet仅对授权用户开放,我在settings.py中配置了全局的认证和权限规则:

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "authentication.token_auth.ExpiringTokenAuthentication"
    ]
}

但调用create_user接口时,却出现了报错:"No token found"。明明ViewSet里的permission_classes应该覆盖全局设置,为什么会出现这种情况?

附自定义认证类代码:

class ExpiringTokenAuthentication(TokenAuthentication):
    def authenticate(self, request):
        if COOKIE_KEY in request.COOKIES:
            token_key = request.COOKIES[COOKIE_KEY]
        else:
            raise exceptions.AuthenticationFailed("No token found")

        try:
            token = Token.objects.get(key=token_key)
        except Token.DoesNotExist:
            return exceptions.AuthenticationFailed("Invalid token")

        if isTokenExpired(token):
            raise exceptions.AuthenticationFailed("Token has expired")

        user = token.user

        return (user, token)


def isTokenExpired(token):
    currentTime = datetime.now(datetime.UTC)
    currentTimeUTC = currentTime.replace(tzinfo=pytz.UTC)
    return token.created < currentTimeUTC - timedelta(hours=TOKEN_EXPIRE_HOURS)

解答

问题出在DRF的执行流程上:认证流程(authentication_classes)的执行早于权限检查(permission_classes)。

你全局配置了ExpiringTokenAuthentication作为默认认证类,所以所有请求都会先执行这个认证逻辑——哪怕你的ViewSet设置了AllowAny权限。而你的自定义认证类在请求Cookie中没有token时,直接抛出了AuthenticationFailed异常,导致请求还没走到权限检查环节就被阻断了。

AllowAny的作用是允许未认证用户访问,但前提是认证流程没有主动抛出异常终止请求。

两种解决方法:

方法1:给认证ViewSet跳过认证流程

直接在AuthenticationViewSet中设置空的authentication_classes,让DRF跳过该ViewSet的认证步骤:

class AuthenticationViewSet(viewsets.ViewSet):
    permission_classes = [AllowAny]
    authentication_classes = []  # 跳过认证流程

    def create_user(self, request: Request) -> Response:
        # 用户创建逻辑代码

方法2:修改自定义认证类的逻辑

让认证类在没有token时返回None而非抛出异常,这样DRF会认为认证失败,但后续权限检查时AllowAny会允许请求继续:

class ExpiringTokenAuthentication(TokenAuthentication):
    def authenticate(self, request):
        if COOKIE_KEY not in request.COOKIES:
            return None  # 没有token时返回None,不抛出异常

        token_key = request.COOKIES[COOKIE_KEY]
        try:
            token = Token.objects.get(key=token_key)
        except Token.DoesNotExist:
            raise exceptions.AuthenticationFailed("Invalid token")

        if isTokenExpired(token):
            raise exceptions.AuthenticationFailed("Token has expired")

        user = token.user
        return (user, token)

内容的提问来源于stack exchange,提问作者U. Watt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 20:25:16