| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- """
- 动态HMAC密钥服务
- 处理动态HMAC密钥的生成、存储、验证等功能
- """
- import secrets
- import base64
- from datetime import datetime, timedelta
- from typing import Tuple, Optional, Dict, Any
- from sqlalchemy.orm import Session
- from sqlalchemy import and_
- from app.models.dynamic_hmac_key import DynamicHmacKey
- from app.core.logger import logger
- from app.utils.timezone import beijing_now_naive
- class DynamicHmacKeyService:
- """动态HMAC密钥服务"""
-
- def __init__(self, db: Session):
- self.db = db
-
- def generate_hmac_key(self) -> str:
- """
- 生成随机HMAC密钥
-
- Returns:
- Base64编码的HMAC密钥
- """
- # 生成32字节的随机密钥
- key_bytes = secrets.token_bytes(32)
- return base64.b64encode(key_bytes).decode('utf-8')
-
- def create_or_update_hmac_key(
- self,
- user_id: int,
- machine_code: str,
- key_version: int = 1,
- expiry_hours: int = 24
- ) -> Tuple[bool, str, Dict[str, Any]]:
- """
- 创建或更新用户的动态HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码
- key_version: 密钥版本
- expiry_hours: 过期时间(小时)
-
- Returns:
- (success, message, data)
- """
- try:
- # 检查是否已存在密钥
- existing = self.db.query(DynamicHmacKey).filter(
- and_(
- DynamicHmacKey.user_id == user_id,
- DynamicHmacKey.machine_code == machine_code,
- DynamicHmacKey.is_deleted == 0
- )
- ).first()
-
- # 生成新密钥
- hmac_key = self.generate_hmac_key()
- expired_at = beijing_now_naive() + timedelta(hours=expiry_hours)
-
- if existing:
- # 更新现有密钥
- existing.hmac_key = hmac_key
- existing.key_version = key_version
- existing.expired_at = expired_at
- existing.updated_at = beijing_now_naive()
- existing.is_active = True
-
- logger.info(f"更新用户动态HMAC密钥: user_id={user_id}, machine_code={machine_code}")
- else:
- # 创建新密钥
- new_key = DynamicHmacKey(
- user_id=user_id,
- hmac_key=hmac_key,
- machine_code=machine_code,
- key_version=key_version,
- expired_at=expired_at,
- is_active=True
- )
- self.db.add(new_key)
-
- logger.info(f"创建用户动态HMAC密钥: user_id={user_id}, machine_code={machine_code}")
-
- self.db.commit()
-
- return True, "密钥同步成功", {
- "key_version": key_version,
- "expired_at": expired_at.isoformat()
- }
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"同步HMAC密钥失败: {e}")
- return False, f"同步HMAC密钥失败: {str(e)}", {}
-
- def get_hmac_key(
- self,
- user_id: int,
- machine_code: str
- ) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
- """
- 获取用户的动态HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码
-
- Returns:
- (success, message, key_data)
- """
- try:
- key_record = self.db.query(DynamicHmacKey).filter(
- and_(
- DynamicHmacKey.user_id == user_id,
- DynamicHmacKey.machine_code == machine_code,
- DynamicHmacKey.is_active == True,
- DynamicHmacKey.is_deleted == 0
- )
- ).first()
-
- if not key_record:
- return False, "未找到有效的HMAC密钥", None
-
- # 检查是否过期
- if key_record.is_expired():
- return False, "HMAC密钥已过期", None
-
- return True, "获取成功", {
- "hmac_key": key_record.hmac_key,
- "key_version": key_record.key_version,
- "expired_at": key_record.expired_at.isoformat() if key_record.expired_at else None
- }
-
- except Exception as e:
- logger.exception(f"获取HMAC密钥失败: {e}")
- return False, f"获取HMAC密钥失败: {str(e)}", None
-
- def verify_hmac_key(
- self,
- user_id: int,
- machine_code: str,
- hmac_key: str
- ) -> Tuple[bool, str]:
- """
- 验证HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码
- hmac_key: 待验证的HMAC密钥
-
- Returns:
- (success, message)
- """
- try:
- success, message, key_data = self.get_hmac_key(user_id, machine_code)
-
- if not success:
- return False, message
-
- if key_data is None:
- return False, "密钥数据为空"
-
- # 验证密钥是否匹配
- stored_key = key_data.get("hmac_key", "")
- if stored_key != hmac_key:
- return False, "HMAC密钥不匹配"
-
- return True, "密钥验证成功"
-
- except Exception as e:
- logger.exception(f"验证HMAC密钥失败: {e}")
- return False, f"验证HMAC密钥失败: {str(e)}"
-
- def revoke_hmac_key(
- self,
- user_id: int,
- machine_code: str
- ) -> Tuple[bool, str]:
- """
- 撤销用户的动态HMAC密钥
-
- Args:
- user_id: 用户ID
- machine_code: 机器码
-
- Returns:
- (success, message)
- """
- try:
- result = self.db.query(DynamicHmacKey).filter(
- and_(
- DynamicHmacKey.user_id == user_id,
- DynamicHmacKey.machine_code == machine_code,
- DynamicHmacKey.is_deleted == 0
- )
- ).update({
- "is_active": False,
- "is_deleted": True,
- "updated_at": beijing_now_naive()
- })
-
- self.db.commit()
-
- if result > 0:
- logger.info(f"撤销用户HMAC密钥: user_id={user_id}, machine_code={machine_code}")
- return True, "密钥撤销成功"
- else:
- return False, "未找到需要撤销的密钥"
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"撤销HMAC密钥失败: {e}")
- return False, f"撤销HMAC密钥失败: {str(e)}"
-
- def revoke_all_user_keys(self, user_id: int) -> Tuple[bool, str, int]:
- """
- 撤销用户的所有动态HMAC密钥
-
- Args:
- user_id: 用户ID
-
- Returns:
- (success, message, revoked_count)
- """
- try:
- result = self.db.query(DynamicHmacKey).filter(
- and_(
- DynamicHmacKey.user_id == user_id,
- DynamicHmacKey.is_deleted == 0
- )
- ).update({
- "is_active": False,
- "is_deleted": True,
- "updated_at": beijing_now_naive()
- })
-
- self.db.commit()
-
- logger.info(f"撤销用户所有HMAC密钥: user_id={user_id}, count={result}")
- return True, f"已撤销 {result} 个密钥", result
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"撤销用户所有HMAC密钥失败: {e}")
- return False, f"撤销失败: {str(e)}", 0
-
- def cleanup_expired_keys(self) -> Tuple[bool, str, int]:
- """
- 清理过期的HMAC密钥
-
- Returns:
- (success, message, cleaned_count)
- """
- try:
- result = self.db.query(DynamicHmacKey).filter(
- and_(
- DynamicHmacKey.is_deleted == 0,
- DynamicHmacKey.expired_at < beijing_now_naive()
- )
- ).update({
- "is_active": False,
- "is_deleted": True,
- "updated_at": beijing_now_naive()
- })
-
- self.db.commit()
-
- if result > 0:
- logger.info(f"清理过期HMAC密钥: count={result}")
-
- return True, f"已清理 {result} 个过期密钥", result
-
- except Exception as e:
- self.db.rollback()
- logger.exception(f"清理过期HMAC密钥失败: {e}")
- return False, f"清理失败: {str(e)}", 0
|