70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
"""项目自定义认证后端
|
||
|
||
SimpleJWT 的 JWTAuthentication 在 __init__ 中调用 django.contrib.auth.get_user_model(),
|
||
返回 AUTH_USER_MODEL 指定的模型(未配置时为 Django 默认 auth.User)。
|
||
|
||
本项目使用 gvsds.apps.core.models.User 作为用户模型(未设置 AUTH_USER_MODEL),
|
||
因此需要覆盖 user_model,使 JWT 认证查询正确的用户表。
|
||
|
||
同时支持从 Authorization 请求头和 HttpOnly Cookie 读取 JWT,
|
||
以兼容前端基于 Cookie 的认证方式(credentials: 'include')。
|
||
|
||
额外职责:JWT 认证成功后立即解析当前角色上下文(ResolveRoleContext,
|
||
唯一实现位于 gvsdsdk.auth.middleware,与 CurrentRoleMiddleware 共用)。
|
||
这解决了 Django 中间件在 DRF 认证之前运行、
|
||
导致中间件无法获取已认证用户的问题。
|
||
"""
|
||
|
||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||
|
||
from gvsds.apps.core.models import User
|
||
from gvsdsdk.auth.middleware import ResolveRoleContext
|
||
|
||
#: 存储 Access Token 的 Cookie 名称
|
||
JWT_ACCESS_COOKIE = 'AccessToken'
|
||
#: 存储 Refresh Token 的 Cookie 名称
|
||
JWT_REFRESH_COOKIE = 'RefreshToken'
|
||
|
||
|
||
class CustomJWTAuthentication(JWTAuthentication):
|
||
"""JWT 认证——使用项目自定义 User 模型(core.User)
|
||
|
||
优先从 Authorization 请求头读取 token;
|
||
若请求头无 token,则回退到 HttpOnly Cookie(AccessToken)。
|
||
|
||
认证成功后立即解析当前角色上下文(ResolveRoleContext),
|
||
确保 request.current_role / current_company_uuid / current_tenant_uuid 可用。
|
||
"""
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
self.user_model = User
|
||
|
||
def get_header(self, request):
|
||
"""优先从 Authorization 头获取,回退到 Cookie"""
|
||
header = super().get_header(request)
|
||
if header is not None:
|
||
return header
|
||
# 回退:从 Cookie 读取 access token,构造标准 Bearer 头
|
||
cookie_token = request.COOKIES.get(JWT_ACCESS_COOKIE)
|
||
if cookie_token:
|
||
auth_header_types = self.get_auth_header_types()
|
||
prefix = f'{auth_header_types[0]} '.encode('utf-8') if auth_header_types else b''
|
||
return prefix + cookie_token.encode('utf-8')
|
||
return None
|
||
|
||
def get_auth_header_types(self):
|
||
from rest_framework_simplejwt.settings import api_settings
|
||
return api_settings.AUTH_HEADER_TYPES
|
||
|
||
def authenticate(self, request):
|
||
"""JWT 认证成功后立即解析角色上下文"""
|
||
result = super().authenticate(request)
|
||
if result is None:
|
||
return None
|
||
user, validated_token = result
|
||
# 认证成功后立即解析角色上下文(DRF 会在 authenticate 返回后设置 request.user,
|
||
# 但此时我们已有 user 实例,可直接传入解析)
|
||
ResolveRoleContext(request, user)
|
||
return result
|