feat: generate permission sql for stocktake modules and implement single-device login restriction

This commit is contained in:
DXC
2026-03-20 09:11:54 +08:00
parent faea0379da
commit 4223a95f10
6 changed files with 147 additions and 4 deletions

View File

@ -6,6 +6,59 @@ import logging
import json
def _verify_token_in_redis():
"""
验证当前 Token 是否与 Redis 中存储的 Token 一致(单设备登录互踢)
"""
from app.extensions import redis_client
from flask import current_app
if redis_client is None:
# Redis 不可用,跳过验证
return True
try:
# 获取请求中的 Token
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return True
request_token = auth_header[7:] # 去掉 'Bearer ' 前缀
# 获取当前用户 ID
claims = get_jwt()
user_id = claims.get('sub')
if user_id is None:
return True
# 从 Redis 获取存储的 Token
stored_token = redis_client.get(f"user_token_{user_id}")
# 如果 Redis 中没有存储的 Token可能是旧登录或 Redis 重启),允许通过
if stored_token is None:
return True
# 比较 Token 是否一致
if request_token != stored_token:
current_app.logger.warning(f"Token mismatch for user {user_id}: request token != stored token")
return False
return True
except Exception as e:
current_app.logger.error(f"Redis token verification error: {e}")
# 出错时默认放行,避免影响正常业务
return True
def _raise_token_mismatch_error():
"""抛出 Token 不一致的错误(用于单设备登录互踢)"""
return jsonify({
'msg': '您的账号已在其他设备登录,请重新登录',
'code': 401,
'reason': 'token_mismatch'
}), 401
def role_required(*roles):
"""
自定义装饰器:检查用户角色
@ -44,6 +97,11 @@ def login_required(fn):
except Exception as e:
logging.warning(f"JWT verification failed: {e}")
return jsonify(msg='登录已过期,请重新登录'), 401
# 单设备登录互踢检查
if not _verify_token_in_redis():
return _raise_token_mismatch_error()
return fn(*args, **kwargs)
return decorator
@ -63,6 +121,10 @@ def permission_required(permission_code):
logging.warning(f"JWT verification failed: {e}")
return jsonify(msg='登录已过期,请重新登录'), 401
# 单设备登录互踢检查
if not _verify_token_in_redis():
return _raise_token_mismatch_error()
claims = get_jwt()
user_role = claims.get('role')
# 超级管理员放行 (忽略大小写)