fix(auth): simplejwt blacklist 适配非 auth User;部署 old2/old3 子域与 SPA fallback
- tokens.py 全局 patch RefreshToken.for_user:不写 OutstandingToken、UserUUID 转 str, 修复登录 500(OutstandingToken.user 必须为 auth.User 实例);settings/urls 移除 token_blacklist - core/views 新增 old2/old3 子域配置(走 _base 模板渲染),SPA fallback 支持 /index.html, .html 重定向排除目录请求,修复 invite 子域 302 错跳 - TempLate/index.html 同步 GVSite 最新构建产物(含头像下拉复用 GERP 组件)
This commit is contained in:
1 parent
21354faa44
commit
87643aeb6e
6 files changed
+60
-11
No files matched your search
+3
-3
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>银河锋锐软件开发工作室</title>
|
||||
<title>银河锋锐(GVSDS)</title>
|
||||
<link rel="icon" href="https://hb.cdn.gvsds.com/assets/icon/ICON.png">
|
||||
<script type="module" crossorigin src="/GVSite/index-D6E7GCNx.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/GVSite/index-C4rcZEW_.css">
|
||||
<script type="module" crossorigin src="/GVSite/index-CK-5nual.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/GVSite/index-DRWk7H-y.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>银河锋锐软件开发工作室</title>
|
||||
<link rel="icon" href="/assets/icon/ICON.png">
|
||||
<script type="module" crossorigin src="/GVSite/index-DGlonf97.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/GVSite/index-C4rcZEW_.css">
|
||||
<title>银河锋锐(GVSDS)</title>
|
||||
<link rel="icon" href="https://hb.cdn.gvsds.com/assets/icon/ICON.png">
|
||||
<script type="module" crossorigin src="/GVSite/index-tyrWQVZX.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/GVSite/index-BY1hqLY-.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -62,6 +62,8 @@ SUBDOMAIN_CONFIG = {
|
||||
'vu': {'dir': None, 'root_is_index': False},
|
||||
'links': {'dir': None, 'root_is_index': False},
|
||||
'unclear': {'dir': 'Unclear', 'root_is_index': True},
|
||||
'old2': {'dir': 'Olds/2', 'root_is_index': True, 'base': 'Olds/2/_base'},
|
||||
'old3': {'dir': 'Olds/3', 'root_is_index': True, 'base': 'Olds/3/_base'},
|
||||
}
|
||||
|
||||
|
||||
@@ -269,7 +271,8 @@ class SubdomainView(View):
|
||||
|
||||
# www/invite 子域:旧 .html 页面已被 Vue SPA 取代,去掉后缀重定向到 Vue 路由
|
||||
# 兼容历史链接(如 /login.html、/gallery.html 等被外部页面/脚本引用的旧地址)
|
||||
if subdomain in ('www', 'invite') and file_path.endswith('.html'):
|
||||
# 目录请求(/xxx/ 拼成的 /xxx/index.html)不走旧 .html 链接重定向,交给下方 SPA fallback
|
||||
if subdomain in ('www', 'invite') and file_path.endswith('.html') and not file_path.endswith('/index.html'):
|
||||
new_path = file_path[:-5] or '/'
|
||||
redirect_url = new_path
|
||||
if request.META.get('QUERY_STRING'):
|
||||
@@ -278,7 +281,10 @@ class SubdomainView(View):
|
||||
|
||||
# SPA fallback:前端单页应用(如 GERP Vue)的客户端路由路径(无扩展名)
|
||||
# 找不到对应文件时,回退到 index.html,由前端路由处理
|
||||
if config.get('root_is_index') and '.' not in os.path.basename(file_path):
|
||||
# 目录请求(/xxx/ 被拼成 /xxx/index.html)同样回退 SPA,避免误入 Django 404
|
||||
if config.get('root_is_index') and (
|
||||
'.' not in os.path.basename(file_path) or file_path.endswith('/index.html')
|
||||
):
|
||||
index_resolved = _resolve_file(subdomain, 'index.html')
|
||||
if index_resolved:
|
||||
return _serve_file(index_resolved, subdomain)
|
||||
|
||||
+1
-2
@@ -50,7 +50,6 @@ INSTALLED_APPS = _app_getlist('apps', 'INSTALLED_APPS', fallback=[
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'rest_framework_simplejwt.token_blacklist', # refresh 轮换后旧 token 拉黑(BLACKLIST_AFTER_ROTATION)
|
||||
'corsheaders',
|
||||
'gvsds.apps.core',
|
||||
'gvsds.apps.gerp',
|
||||
@@ -295,7 +294,7 @@ SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(hours=_app_getint('jwt', 'ACCESS_TOKEN_LIFETIME_HOURS', fallback=24)), # 24 小时(收敛自 168h,缩小泄露窗口)
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=_app_getint('jwt', 'REFRESH_TOKEN_LIFETIME_DAYS', fallback=90)), # 90 天
|
||||
'ROTATE_REFRESH_TOKENS': _app_getbool('jwt', 'ROTATE_REFRESH_TOKENS', fallback=True),
|
||||
'BLACKLIST_AFTER_ROTATION': _app_getbool('jwt', 'BLACKLIST_AFTER_ROTATION', fallback=True), # 轮换后旧 refresh 立即失效
|
||||
'BLACKLIST_AFTER_ROTATION': _app_getbool('jwt', 'BLACKLIST_AFTER_ROTATION', fallback=False), # 已禁用 blacklist(业务 User 非 Django auth 模型,OutstandingToken 无法落库)
|
||||
'AUTH_HEADER_TYPES': tuple(_app_getlist('jwt', 'AUTH_HEADER_TYPES', fallback=['Bearer'])),
|
||||
'USER_ID_FIELD': 'UserUUID',
|
||||
'USER_ID_CLAIM': 'UserUUID',
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""simplejwt token 适配层
|
||||
|
||||
业务用户模型 core.User 非 Django auth 模型(项目未设置 AUTH_USER_MODEL),
|
||||
simplejwt blacklist 的 OutstandingToken.user(FK -> auth.User)无法承接 core.User
|
||||
实例,登录签发 token 时触发 ValueError。
|
||||
此处将 blacklist 相关操作全部 no-op,并全局替换 RefreshToken.for_user,
|
||||
签发时不再落 OutstandingToken 黑名单表。
|
||||
"""
|
||||
|
||||
from rest_framework_simplejwt.settings import api_settings
|
||||
from rest_framework_simplejwt.tokens import RefreshToken as _BaseRefreshToken
|
||||
|
||||
|
||||
def _for_user(cls, user):
|
||||
"""签发 refresh token(写 jti/exp),但不落 OutstandingToken 黑名单表"""
|
||||
token = cls()
|
||||
token[api_settings.USER_ID_CLAIM] = str(getattr(user, api_settings.USER_ID_FIELD)) # UUIDObj 需转 str 才能 JSON 序列化
|
||||
token.set_jti()
|
||||
token.set_exp()
|
||||
return token
|
||||
|
||||
|
||||
class RefreshToken(_BaseRefreshToken):
|
||||
"""业务版 RefreshToken:blacklist 相关操作全部 no-op"""
|
||||
|
||||
for_user = classmethod(_for_user)
|
||||
|
||||
def check_blacklist(self):
|
||||
return None
|
||||
|
||||
def blacklist(self):
|
||||
return None
|
||||
|
||||
def outstand(self):
|
||||
return None
|
||||
|
||||
|
||||
# 全局替换 simplejwt 原版类方法,覆盖 TokenObtainPairView / TokenRefreshView 等内部调用
|
||||
_BaseRefreshToken.for_user = classmethod(_for_user)
|
||||
_BaseRefreshToken.check_blacklist = lambda self: None
|
||||
_BaseRefreshToken.blacklist = lambda self: None
|
||||
_BaseRefreshToken.outstand = lambda self: None
|
||||
@@ -1,3 +1,5 @@
|
||||
import gvsds.tokens # noqa: F401 # simplejwt blacklist 适配(签发 token 不落 OutstandingToken 表)
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import path, include, re_path
|
||||
from django.contrib import admin
|
||||
|
||||
Reference in new issue
Block a user