Apache代理下DRF的Swagger UI遇Basic Auth 403禁止错误
问题:Apache添加Basic Auth后Django DRF Swagger UI返回403 Forbidden
环境背景
- 部署Django Rest Framework应用,后端运行在gunicorn(端口8000)
- 采用Apache做反向代理(因应用部分模块的认证需求必须使用Apache)
正常运行的Apache配置
<Location "/api/v1/schema"> Require all granted # ProxyPass http://localhost:8000/api/v1/schema ProxyPassReverse http://localhost:8000/api/v1/schema </Location>
添加Basic Auth后的配置(出现403错误)
<Location "/api/v1/schema"> AuthType Basic AuthName "MyAPI Swagger UI" AuthUserFile /etc/apache2/swagger.htpasswd Require valid-user # ProxyPass http://localhost:8000/api/v1/schema ProxyPassReverse http://localhost:8000/api/v1/schema </Location>
错误日志
Apache日志
myapi.example.com:443 130.211.2.57 - swagger [21/Jul/2024:18:51:59 +0000] "GET /api/v1/schema/swagger-ui/ HTTP/1.1" 403 378 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
gunicorn日志
Forbidden: /api/v1/schema/swagger-ui/
Django DRF相关配置
REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', ], 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], }
更新信息
执行showurls命令获取到对应路由:
/api/v1/schema/swagger-ui/ drf_spectacular.views.SpectacularSwaggerView swagger-ui
查看drf_spectacular模块中views.py的对应视图代码:
class SpectacularSwaggerView(APIView): renderer_classes = [TemplateHTMLRenderer] permission_classes = spectacular_settings.SERVE_PERMISSIONS authentication_classes = AUTHENTICATION_CLASSES url_name: str = 'schema' url: Optional[str] = None template_name: str = 'drf_spectacular/swagger_ui.html' template_name_js: str = 'drf_spectacular/swagger_ui.js' title: str = spectacular_settings.TITLE @extend_schema(exclude=True) def get(self, request, *args, **kwargs): return Response( data={ 'title': self.title, 'swagger_ui_css': self._swagger_ui_resource('swagger-ui.css'), 'swagger_ui_bundle': self._swagger_ui_resource('swagger-ui-bundle.js'), 'swagger_ui_standalone': self._swagger_ui_resource('swagger-ui-standalone-preset.js'), 'favicon_href': self._swagger_ui_favicon(), 'schema_url': self._get_schema_url(request), 'settings': self._dump(spectacular_settings.SWAGGER_UI_SETTINGS), 'oauth2_config': self._dump(spectacular_settings.SWAGGER_UI_OAUTH2_CONFIG), 'template_name_js': self.template_name_js, 'csrf_header_name': self._get_csrf_header_name(), 'schema_auth_names': self._dump(self._get_schema_auth_names()), }, template_name=self.template_name, headers={ "Cross-Origin-Opener-Policy": "unsafe-none", } ) def _dump(self, data): return data if isinstance(data, str) else json.dumps(data, indent=2) def _get_schema_url(self, request): schema_url = self.url or get_relative_url(reverse(self.url_name, request=request)) return set_query_parameters( url=schema_url, lang=request.GET.get('lang'), version=request.GET.get('version') ) def _get_csrf_header_name(self): csrf_header_name = settings.CSRF_HEADER_NAME if csrf_header_name.startswith('HTTP_'): csrf_header_name = csrf_header_name[5:] return csrf_header_name.replace('_', '-') def _get_schema_auth_names(self): from drf_spectacular.extensions import OpenApiAuthenticationExtension if spectacular_settings.SERVE_PUBLIC: return [] auth_extensions = [ OpenApiAuthenticationExtension.get_match(klass) for klass in self.authentication_classes ] return [auth.name for auth in auth_extensions if auth] @staticmethod def _swagger_ui_resource(filename): if spectacular_settings.SWAGGER_UI_DIST == 'SIDECAR': return _get_sidecar_url(f'swagger-ui-dist/{filename}') return f'{spectacular_settings.SWAGGER_UI_DIST}/{filename}' @staticmethod def _swagger_ui_favicon(): if spectacular_settings.SWAGGER_UI_FAVICON_HREF == 'SIDECAR': return _get_sidecar_url('swagger-ui-dist/favicon-32x32.png') return spectacular_settings.SWAGGER_UI_FAVICON_HREF
解决方案
问题根源是Apache会将Basic Auth的Authorization请求头转发给Django,而SpectacularSwaggerView默认继承了DRF的认证配置,会尝试验证该头信息,但Django中不存在Apache htpasswd里的用户,因此返回403。以下两种方法可解决:
方法1:修改Apache配置,不传递Authorization头给Django
让Apache完成Basic Auth验证后,移除请求头中的Authorization信息再转发给gunicorn:
<Location "/api/v1/schema"> AuthType Basic AuthName "MyAPI Swagger UI" AuthUserFile /etc/apache2/swagger.htpasswd Require valid-user # 移除Authorization头 RequestHeader unset Authorization ProxyPass http://localhost:8000/api/v1/schema nocanon ProxyPassReverse http://localhost:8000/api/v1/schema </Location>
方法2:修改drf-spectacular配置,允许匿名访问Swagger UI
在Django的settings.py中添加配置,强制Swagger UI视图允许匿名访问:
SPECTACULAR_SETTINGS = { 'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'], 'SERVE_AUTHENTICATION': [], }
内容的提问来源于stack exchange,提问作者rlandster
相关产品推荐
相关产品推荐

