dynamic_hmac_service.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. """
  2. 动态HMAC密钥服务
  3. 处理动态HMAC密钥的生成、存储、验证等功能
  4. """
  5. import secrets
  6. import base64
  7. from datetime import datetime, timedelta
  8. from typing import Tuple, Optional, Dict, Any
  9. from sqlalchemy.orm import Session
  10. from sqlalchemy import and_
  11. from app.models.dynamic_hmac_key import DynamicHmacKey
  12. from app.core.logger import logger
  13. from app.utils.timezone import beijing_now_naive
  14. class DynamicHmacKeyService:
  15. """动态HMAC密钥服务"""
  16. def __init__(self, db: Session):
  17. self.db = db
  18. def generate_hmac_key(self) -> str:
  19. """
  20. 生成随机HMAC密钥
  21. Returns:
  22. Base64编码的HMAC密钥
  23. """
  24. # 生成32字节的随机密钥
  25. key_bytes = secrets.token_bytes(32)
  26. return base64.b64encode(key_bytes).decode('utf-8')
  27. def create_or_update_hmac_key(
  28. self,
  29. user_id: int,
  30. machine_code: str,
  31. key_version: int = 1,
  32. expiry_hours: int = 24
  33. ) -> Tuple[bool, str, Dict[str, Any]]:
  34. """
  35. 创建或更新用户的动态HMAC密钥
  36. Args:
  37. user_id: 用户ID
  38. machine_code: 机器码
  39. key_version: 密钥版本
  40. expiry_hours: 过期时间(小时)
  41. Returns:
  42. (success, message, data)
  43. """
  44. try:
  45. # 检查是否已存在密钥
  46. existing = self.db.query(DynamicHmacKey).filter(
  47. and_(
  48. DynamicHmacKey.user_id == user_id,
  49. DynamicHmacKey.machine_code == machine_code,
  50. DynamicHmacKey.is_deleted == 0
  51. )
  52. ).first()
  53. # 生成新密钥
  54. hmac_key = self.generate_hmac_key()
  55. expired_at = beijing_now_naive() + timedelta(hours=expiry_hours)
  56. if existing:
  57. # 更新现有密钥
  58. existing.hmac_key = hmac_key
  59. existing.key_version = key_version
  60. existing.expired_at = expired_at
  61. existing.updated_at = beijing_now_naive()
  62. existing.is_active = True
  63. logger.info(f"更新用户动态HMAC密钥: user_id={user_id}, machine_code={machine_code}")
  64. else:
  65. # 创建新密钥
  66. new_key = DynamicHmacKey(
  67. user_id=user_id,
  68. hmac_key=hmac_key,
  69. machine_code=machine_code,
  70. key_version=key_version,
  71. expired_at=expired_at,
  72. is_active=True
  73. )
  74. self.db.add(new_key)
  75. logger.info(f"创建用户动态HMAC密钥: user_id={user_id}, machine_code={machine_code}")
  76. self.db.commit()
  77. return True, "密钥同步成功", {
  78. "key_version": key_version,
  79. "expired_at": expired_at.isoformat()
  80. }
  81. except Exception as e:
  82. self.db.rollback()
  83. logger.exception(f"同步HMAC密钥失败: {e}")
  84. return False, f"同步HMAC密钥失败: {str(e)}", {}
  85. def get_hmac_key(
  86. self,
  87. user_id: int,
  88. machine_code: str
  89. ) -> Tuple[bool, str, Optional[Dict[str, Any]]]:
  90. """
  91. 获取用户的动态HMAC密钥
  92. Args:
  93. user_id: 用户ID
  94. machine_code: 机器码
  95. Returns:
  96. (success, message, key_data)
  97. """
  98. try:
  99. key_record = self.db.query(DynamicHmacKey).filter(
  100. and_(
  101. DynamicHmacKey.user_id == user_id,
  102. DynamicHmacKey.machine_code == machine_code,
  103. DynamicHmacKey.is_active == True,
  104. DynamicHmacKey.is_deleted == 0
  105. )
  106. ).first()
  107. if not key_record:
  108. return False, "未找到有效的HMAC密钥", None
  109. # 检查是否过期
  110. if key_record.is_expired():
  111. return False, "HMAC密钥已过期", None
  112. return True, "获取成功", {
  113. "hmac_key": key_record.hmac_key,
  114. "key_version": key_record.key_version,
  115. "expired_at": key_record.expired_at.isoformat() if key_record.expired_at else None
  116. }
  117. except Exception as e:
  118. logger.exception(f"获取HMAC密钥失败: {e}")
  119. return False, f"获取HMAC密钥失败: {str(e)}", None
  120. def verify_hmac_key(
  121. self,
  122. user_id: int,
  123. machine_code: str,
  124. hmac_key: str
  125. ) -> Tuple[bool, str]:
  126. """
  127. 验证HMAC密钥
  128. Args:
  129. user_id: 用户ID
  130. machine_code: 机器码
  131. hmac_key: 待验证的HMAC密钥
  132. Returns:
  133. (success, message)
  134. """
  135. try:
  136. success, message, key_data = self.get_hmac_key(user_id, machine_code)
  137. if not success:
  138. return False, message
  139. if key_data is None:
  140. return False, "密钥数据为空"
  141. # 验证密钥是否匹配
  142. stored_key = key_data.get("hmac_key", "")
  143. if stored_key != hmac_key:
  144. return False, "HMAC密钥不匹配"
  145. return True, "密钥验证成功"
  146. except Exception as e:
  147. logger.exception(f"验证HMAC密钥失败: {e}")
  148. return False, f"验证HMAC密钥失败: {str(e)}"
  149. def revoke_hmac_key(
  150. self,
  151. user_id: int,
  152. machine_code: str
  153. ) -> Tuple[bool, str]:
  154. """
  155. 撤销用户的动态HMAC密钥
  156. Args:
  157. user_id: 用户ID
  158. machine_code: 机器码
  159. Returns:
  160. (success, message)
  161. """
  162. try:
  163. result = self.db.query(DynamicHmacKey).filter(
  164. and_(
  165. DynamicHmacKey.user_id == user_id,
  166. DynamicHmacKey.machine_code == machine_code,
  167. DynamicHmacKey.is_deleted == 0
  168. )
  169. ).update({
  170. "is_active": False,
  171. "is_deleted": True,
  172. "updated_at": beijing_now_naive()
  173. })
  174. self.db.commit()
  175. if result > 0:
  176. logger.info(f"撤销用户HMAC密钥: user_id={user_id}, machine_code={machine_code}")
  177. return True, "密钥撤销成功"
  178. else:
  179. return False, "未找到需要撤销的密钥"
  180. except Exception as e:
  181. self.db.rollback()
  182. logger.exception(f"撤销HMAC密钥失败: {e}")
  183. return False, f"撤销HMAC密钥失败: {str(e)}"
  184. def revoke_all_user_keys(self, user_id: int) -> Tuple[bool, str, int]:
  185. """
  186. 撤销用户的所有动态HMAC密钥
  187. Args:
  188. user_id: 用户ID
  189. Returns:
  190. (success, message, revoked_count)
  191. """
  192. try:
  193. result = self.db.query(DynamicHmacKey).filter(
  194. and_(
  195. DynamicHmacKey.user_id == user_id,
  196. DynamicHmacKey.is_deleted == 0
  197. )
  198. ).update({
  199. "is_active": False,
  200. "is_deleted": True,
  201. "updated_at": beijing_now_naive()
  202. })
  203. self.db.commit()
  204. logger.info(f"撤销用户所有HMAC密钥: user_id={user_id}, count={result}")
  205. return True, f"已撤销 {result} 个密钥", result
  206. except Exception as e:
  207. self.db.rollback()
  208. logger.exception(f"撤销用户所有HMAC密钥失败: {e}")
  209. return False, f"撤销失败: {str(e)}", 0
  210. def cleanup_expired_keys(self) -> Tuple[bool, str, int]:
  211. """
  212. 清理过期的HMAC密钥
  213. Returns:
  214. (success, message, cleaned_count)
  215. """
  216. try:
  217. result = self.db.query(DynamicHmacKey).filter(
  218. and_(
  219. DynamicHmacKey.is_deleted == 0,
  220. DynamicHmacKey.expired_at < beijing_now_naive()
  221. )
  222. ).update({
  223. "is_active": False,
  224. "is_deleted": True,
  225. "updated_at": beijing_now_naive()
  226. })
  227. self.db.commit()
  228. if result > 0:
  229. logger.info(f"清理过期HMAC密钥: count={result}")
  230. return True, f"已清理 {result} 个过期密钥", result
  231. except Exception as e:
  232. self.db.rollback()
  233. logger.exception(f"清理过期HMAC密钥失败: {e}")
  234. return False, f"清理失败: {str(e)}", 0