1
0
forked from erp-dev/erp
Files
erpnew/sse/auth_utils.py

117 lines
3.6 KiB
Python
Raw 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.
"""
SSE认证工具模块
提供统一的认证和商户验证功能用于SSE连接端点和其他需要认证的地方
"""
from django.http import HttpResponseForbidden
from rest_framework.authentication import get_authorization_header
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken
from rest_framework.exceptions import AuthenticationFailed
import logging
logger = logging.getLogger(__name__)
def get_user_merchant_id(request):
"""获取用户所属商户ID"""
try:
# 检查用户是否已认证
if not request.user.is_authenticated:
return None
# 获取商户ID
return request.user.employee.merchant_id
except (AttributeError, AttributeError):
return None
def authenticate_sse_request(request):
"""
对SSE连接请求进行认证
参数:
- request: Django HTTP请求对象
返回:
- tuple: (user, merchant_id) 认证成功返回用户和商户ID
- None: 认证失败返回None
"""
jwt_authenticator = JWTAuthentication()
try:
# 获取Authorization头
auth_header = get_authorization_header(request).split()
if not auth_header or auth_header[0].lower() != b'bearer':
logger.warning("SSE认证失败: 缺少Bearer token")
return None
if len(auth_header) < 2:
logger.warning("SSE认证失败: Bearer token格式错误")
return None
token = auth_header[1].decode('utf-8')
# 验证token
validated_token = jwt_authenticator.get_validated_token(token)
user = jwt_authenticator.get_user(validated_token)
# 检查用户是否有关联的员工和商户
try:
merchant_id = user.employee.merchant_id
if not merchant_id:
logger.warning(f"用户 {user.username} 无关联商户")
return None
return user, merchant_id
except AttributeError:
logger.warning(f"用户 {user.username} 无关联员工")
return None
except (IndexError, InvalidToken, AuthenticationFailed) as e:
logger.warning(f"SSE认证失败: {str(e)}")
return None
def require_sse_authentication(view_func):
"""
装饰器要求SSE连接认证
用于SSE连接端点的认证如果认证失败则返回403响应
OPTIONS请求跳过认证
"""
def wrapper(request, *args, **kwargs):
# OPTIONS请求跳过认证
if request.method == 'OPTIONS':
return view_func(request, *args, **kwargs)
auth_result = authenticate_sse_request(request)
if not auth_result:
return HttpResponseForbidden("Authentication failed or user has no associated merchant")
user, merchant_id = auth_result
request.user = user
request.merchant_id = merchant_id
return view_func(request, *args, **kwargs)
return wrapper
def validate_sse_request(request):
"""
验证SSE请求的认证状态
返回:
- tuple: (is_valid, error_response)
- is_valid: bool 表示请求是否有效
- error_response: HttpResponse 如果无效则返回错误响应否则为None
"""
auth_result = authenticate_sse_request(request)
if not auth_result:
return False, HttpResponseForbidden("Authentication failed or user has no associated merchant")
user, merchant_id = auth_result
request.user = user
request.merchant_id = merchant_id
return True, None