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
相关产品推荐
相关产品推荐

