From 5c8b82df156579cd30baac08a539ed1298426f03 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 18 Jun 2026 19:17:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86token=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- a_long_dianjing/settings.py | 2 +- check_prod_uid.py | 27 +++++++++++++++++++ gvsdsdk/auth/jwt_auth.py | 54 +++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 check_prod_uid.py create mode 100644 gvsdsdk/auth/jwt_auth.py diff --git a/a_long_dianjing/settings.py b/a_long_dianjing/settings.py index 38898a6..ac30768 100644 --- a/a_long_dianjing/settings.py +++ b/a_long_dianjing/settings.py @@ -166,7 +166,7 @@ AUTH_USER_MODEL = 'gvsdsdk.User' # ==================== DRF 配置 ==================== REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_simplejwt.authentication.JWTAuthentication', + 'gvsdsdk.auth.jwt_auth.CompatibleJWTAuthentication', 'gvsdsdk.auth.elt_auth.ELTAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( diff --git a/check_prod_uid.py b/check_prod_uid.py new file mode 100644 index 0000000..9dfab53 --- /dev/null +++ b/check_prod_uid.py @@ -0,0 +1,27 @@ +"""检查生产库中 UserUID 为空的用户""" +import pymysql + +conn = pymysql.connect( + host='gvsds.com', port=50030, + user='root', password='sajksh.sdfGH3YUge.wjkd+', + database='xaio_cheng_xu', charset='utf8mb4' +) +cur = conn.cursor() + +# UserUID 为 NULL 的用户数 +cur.execute("SELECT COUNT(*) FROM user WHERE UserUID IS NULL OR UserUID = ''") +print(f'UserUID为空的用户数: {cur.fetchone()[0]}') + +# 总用户数 +cur.execute("SELECT COUNT(*) FROM user") +print(f'总用户数: {cur.fetchone()[0]}') + +# 抽样 UserUID 为 NULL 的用户 +cur.execute("SELECT UserUUID, UserName, OpenID, Phone FROM user WHERE UserUID IS NULL OR UserUID = '' LIMIT 5") +rows = cur.fetchall() +if rows: + print('\nUserUID为空的用户示例:') + for r in rows: + print(f' UserName={r[1]}, OpenID={r[2]}, Phone={r[3]}') + +conn.close() diff --git a/gvsdsdk/auth/jwt_auth.py b/gvsdsdk/auth/jwt_auth.py new file mode 100644 index 0000000..5d9f108 --- /dev/null +++ b/gvsdsdk/auth/jwt_auth.py @@ -0,0 +1,54 @@ +"""自定义 JWT 认证 — 向后兼容旧 token 的 user_id claim""" + +from rest_framework_simplejwt.authentication import JWTAuthentication +from rest_framework_simplejwt.exceptions import InvalidToken, AuthenticationFailed +from rest_framework_simplejwt.settings import api_settings +from django.utils.translation import gettext_lazy as _ + + +class CompatibleJWTAuthentication(JWTAuthentication): + """ + 兼容旧 token 的 JWT 认证类。 + + 旧 token(USER_ID_CLAIM='user_id' 时生成)payload 中使用 'user_id' claim, + 值为 user.id(UUID 字符串)。 + 新 token(USER_ID_CLAIM='UserUID' 时生成)payload 中使用 'UserUID' claim, + 值为 user.UserUID(7位业务ID)。 + + 此类先尝试新 claim,找不到时回退到旧 claim,并用对应字段查库。 + """ + + def get_user(self, validated_token): + # 1. 先尝试当前配置的 USER_ID_CLAIM + user_id = validated_token.get(api_settings.USER_ID_CLAIM) + lookup_field = api_settings.USER_ID_FIELD + + if user_id is None: + # 2. 回退:尝试旧 token 中的 'user_id' claim + user_id = validated_token.get('user_id') + if user_id is None: + raise InvalidToken( + {'detail': _('令牌未包含用户标识符'), 'code': 'token_not_valid'} + ) + # 旧 token 的 user_id 来自 user.id(即 UserUUID), + # 需要用 UserUUID 字段查库 + lookup_field = 'UserUUID' + + try: + user = self.user_model.objects.get(**{lookup_field: user_id}) + except self.user_model.DoesNotExist: + raise AuthenticationFailed(_('用户不存在'), code='user_not_found') + + if not user.is_active: + raise AuthenticationFailed(_('用户已被禁用'), code='user_inactive') + + if api_settings.CHECK_REVOKE_TOKEN: + from rest_framework_simplejwt.utils import get_md5_hash_password + if validated_token.get( + api_settings.REVOKE_TOKEN_CLAIM + ) != get_md5_hash_password(user.password): + raise AuthenticationFailed( + _('用户密码已更改'), code='password_changed' + ) + + return user