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

Django-DRF类视图使用Authlib ResourceProtector报错:无build_absolute_uri属性

在Django-DRF类视图中使用Authlib ResourceProtector解决OAuth认证问题

问题场景

用Authlib结合Auth0给Django-DRF API做OAuth2.0认证时,函数视图用@require_oauth()装饰完全正常,但给ModelViewSet的方法(比如list)加这个装饰器时,会抛出错误:

AttributeError: 'StatisticsViewSet' object has no attribute 'build_absolute_uri'

原代码示例

from api.permissions import auth0_validator
from authlib.integrations.django_oauth2 import ResourceProtector
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
import os

require_oauth = ResourceProtector()
validator = auth0_validator.Auth0JWTBearerTokenValidator(
    os.environ['AUTH0_DOMAIN'],
    os.environ['AUTH0_IDENTIFIER']
)
require_oauth.register_token_validator(validator)

# 函数视图正常工作
@require_oauth()
def index(request):
    return Response('Access granted')

# 类视图方法装饰后报错
class Users(ModelViewSet):
    @require_oauth()
    def list(self, request):
        return Response('access granted')

错误堆栈

Internal Server Error: /v2/statistics
Traceback (most recent call last):
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/sentry_sdk/integrations/django/views.py", line 68, in sentry_wrapped_callback
    return callback(request, *args, **kwargs)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/authlib/integrations/django_oauth2/resource_protector.py", line 39, in decorated
    token = self.acquire_token(request, scopes)
  File "/Users/td/Desktop/test-api/lib/python3.8/site-packages/authlib/integrations/django_oauth2/resource_protector.py", line 25, in acquire_token
    url = request.build_absolute_uri()
AttributeError: 'StatisticsViewSet' object has no attribute 'build_absolute_uri'

错误原因

类视图的方法第一个参数是self(ViewSet实例),而Authlib的@require_oauth()装饰器默认认为第一个参数是Django HttpRequest对象,装饰后把self当成了request传入,调用build_absolute_uri()时自然找不到这个方法——这个方法是HttpRequest的专属方法,ViewSet实例没有。

解决方案

方案1:用method_decorator适配类视图方法

用Django的method_decorator把Authlib的装饰器转换成适配类视图的形式,它会自动调整参数顺序,把request正确传递给装饰器。

写法1:直接装饰类方法

from django.utils.decorators import method_decorator

class Users(ModelViewSet):
    @method_decorator(require_oauth())
    def list(self, request):
        return Response('access granted')

写法2:在类上装饰指定方法

如果要给多个方法加装饰,这种写法更整洁:

from django.utils.decorators import method_decorator

@method_decorator(require_oauth(), name='list')
@method_decorator(require_oauth(), name='retrieve')
class Users(ModelViewSet):
    def list(self, request):
        return Response('access granted')
    
    def retrieve(self, request, pk=None):
        return Response('access granted')

方案2:封装成DRF权限类(推荐)

DRF本身更推荐用权限类来统一处理认证授权,把Authlib的验证逻辑封装成BasePermission子类,这样不用给每个方法单独加装饰,还能统一管理权限规则。

from rest_framework.permissions import BasePermission

# 先定义权限类
class OAuth2Permission(BasePermission):
    def has_permission(self, request, view):
        try:
            # 调用Authlib的token验证逻辑
            require_oauth.acquire_token(request)
            return True
        except Exception:
            # 验证失败返回False,DRF会自动返回401/403响应
            return False

# 在ViewSet中使用
class Users(ModelViewSet):
    permission_classes = [OAuth2Permission]  # 直接添加权限类
    
    def list(self, request):
        return Response('access granted')

这种方式更贴合DRF的设计思路,而且可以轻松扩展(比如添加scope验证、角色检查等)。


内容的提问来源于stack exchange,提问作者Tamdim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 20:01:13