feat: generate permission sql for stocktake modules and implement single-device login restriction
This commit is contained in:
@ -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')
|
||||
# 超级管理员放行 (忽略大小写)
|
||||
|
||||
Reference in New Issue
Block a user