如何为drf-spectacular的Swagger页面配置自定义Staff权限认证?
解决drf-spectacular中Swagger页面Staff权限认证不生效问题
问题背景
项目从drf-yasg迁移至drf-spectacular(因drf-yasg不支持OpenAPI 3.0+),此前在drf-yasg中通过设置authentication_classes可实现Swagger页面的Staff权限认证,但迁移后配置自定义isStaffPermission认证类无法生效,需实现Swagger页面加载前弹出登录验证。
原drf-yasg实现代码
schema_view = get_schema_view( openapi.Info( title="API", default_version="v2", description="api description", terms_of_service="", contact=openapi.Contact(email=""), license=openapi.License(name=""), ), public=True, patterns=swagger_urls, authentication_classes=(isStaffPermission,), )
当前drf-spectacular无效尝试代码
class MySchemaView(SpectacularAPIView): urlconf=swagger_urls class CustomSpectacularSwaggerView(SpectacularSwaggerView): authentication_classes=(isStaffPermission,) urlpatterns += [ path("api/v3/schema/", MySchemaView.as_view(api_version="v3"), name="schema"), path('api/v3/swagger-ui/', CustomSpectacularSwaggerView.as_view(), name='swagger-ui') ]
自定义认证类代码
class isStaffPermission(authentication.BasicAuthentication): """Custom authentication class to check if the user is staff.""" def authenticate(self, request) -> Optional[User]: """Authenticate the user""" user: Optional[User, bool] = super().authenticate(request) if user and user[0] and user[0].is_staff: return user return None
问题原因
drf-spectacular的SpectacularSwaggerView基于Django的TemplateView,而非DRF的APIView,直接设置authentication_classes不会触发DRF的认证流程。此外,生成API Schema的SpectacularAPIView也需要添加认证保护,否则未授权用户可直接获取Schema信息。
修复方案
1. 保护Schema生成接口
修改MySchemaView,同时配置认证类和权限类,确保只有授权Staff用户能获取Schema:
class MySchemaView(SpectacularAPIView): urlconf = swagger_urls authentication_classes = (isStaffPermission,) permission_classes = (IsAuthenticated,)
2. 保护Swagger UI页面
由于SpectacularSwaggerView是TemplateView,需通过以下两种方式之一实现认证:
方式一:使用Django会话认证装饰器
若项目使用Django的会话登录,可通过login_required装饰器强制用户登录:
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator @method_decorator(login_required, name='dispatch') class CustomSpectacularSwaggerView(SpectacularSwaggerView): pass
方式二:重写dispatch方法实现Basic Auth弹窗
适配自定义的isStaffPermission认证类,触发浏览器Basic Auth弹窗:
from django.http import HttpResponse class CustomSpectacularSwaggerView(SpectacularSwaggerView): def dispatch(self, request, *args, **kwargs): auth_class = isStaffPermission() auth_result = auth_class.authenticate(request) if not auth_result: # 返回401并设置WWW-Authenticate头,触发浏览器登录弹窗 return HttpResponse(status=401, headers={'WWW-Authenticate': 'Basic'}) # 将认证后的用户和认证信息绑定到request request.user = auth_result[0] request.auth = auth_result[1] return super().dispatch(request, *args, **kwargs)
3. 优化自定义认证类(可选)
原认证类返回None时DRF会尝试其他认证类,若要直接拒绝非Staff用户,可抛出AuthenticationFailed异常:
from rest_framework.exceptions import AuthenticationFailed class isStaffPermission(authentication.BasicAuthentication): """Custom authentication class to check if the user is staff.""" def authenticate(self, request) -> Optional[User]: """Authenticate the user""" user_tuple = super().authenticate(request) if not user_tuple: return None user, auth = user_tuple if not user.is_staff: raise AuthenticationFailed("仅允许Staff用户访问") return user_tuple
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

