Files

70 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""项目自定义认证后端
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