JwtService.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?php
  2. namespace app\admin\service;
  3. use app\admin\model\User;
  4. use think\facade\Cache;
  5. use think\facade\Config;
  6. use think\exception\RuntimeException;
  7. use thans\jwt\exception\JWTException;
  8. use thans\jwt\facade\JWT;
  9. class JwtService
  10. {
  11. /**
  12. * Token 过期时间(秒),优先读取 jwt 配置文件
  13. * @var int
  14. */
  15. private $exp;
  16. public function __construct()
  17. {
  18. // 从 thans/jwt 配置文件读取过期时间(config/jwt.php)
  19. $this->exp = Config::get('jwt.ttl', 7200);
  20. }
  21. /**
  22. * 生成 JWT Token(适配 thans/jwt)
  23. * @param User $user 用户模型实例
  24. * @return string
  25. * @throws RuntimeException
  26. */
  27. public function generateToken(User $user): string
  28. {
  29. // 严格校验参数类型
  30. if (!$user instanceof User) {
  31. throw new RuntimeException('生成Token失败:必须传入 User 模型实例');
  32. }
  33. try {
  34. // 构建 JWT 载荷(thans/jwt 支持数组格式)
  35. $payload = [
  36. 'sub' => $user->id, // 主题(用户ID)
  37. 'user_id' => $user->id, // 自定义字段:用户ID
  38. 'iat' => time(), // 签发时间
  39. 'exp' => time() + $this->exp, // 过期时间
  40. 'iss' => Config::get('app.app_name', 'admin_system'), // 签发者
  41. ];
  42. // 生成 Token(thans/jwt 核心方法)
  43. $token = JWT::builder($payload);
  44. // 缓存 Token(区分普通用户/商家)
  45. $cacheKey = $user->is_store == 0
  46. ? "user_{$user->id}_jwt"
  47. : "store_{$user->id}_jwt";
  48. // TP 缓存:set 方法存储,过期时间与 Token 一致
  49. Cache::set($cacheKey, $token, $this->exp);
  50. return $token;
  51. } catch (JWTException $e) {
  52. throw new RuntimeException('生成JWT Token失败:'.$e->getMessage());
  53. }
  54. }
  55. /**
  56. * 验证 JWT Token 有效性(适配 thans/jwt)
  57. * @param string $token JWT Token 字符串(支持带/不带 Bearer 前缀)
  58. * @return array|null 解码后的载荷数组(无效返回null)
  59. */
  60. public function validateToken(string $token): ?array
  61. {
  62. // 预处理 Token:移除 Bearer 前缀
  63. $token = trim(str_replace('Bearer ', '', $token));
  64. if (empty($token)) {
  65. return null;
  66. }
  67. try {
  68. // 验证并解码 Token(thans/jwt 核心方法)
  69. $payload = JWT::verify($token);
  70. // 额外校验:缓存中的 Token 是否一致(防止注销/伪造)
  71. if (isset($payload['user_id'])) {
  72. $user = User::find($payload['user_id']);
  73. if ($user) {
  74. $cacheKey = $user->is_store == 0
  75. ? "user_{$user->id}_jwt"
  76. : "store_{$user->id}_jwt";
  77. $cacheToken = Cache::get($cacheKey);
  78. // 缓存无 Token 或不一致,视为无效
  79. if ($cacheToken !== $token) {
  80. return null;
  81. }
  82. }
  83. }
  84. return (array)$payload;
  85. } catch (JWTException $e) {
  86. // 捕获 thans/jwt 专属异常(过期、签名错误、格式错误等)
  87. return null;
  88. }
  89. }
  90. /**
  91. * 注销 Token(清除缓存)
  92. * @param User $user 用户模型实例
  93. * @return bool
  94. */
  95. public function invalidateToken(User $user): bool
  96. {
  97. $cacheKey = $user->is_store == 0
  98. ? "user_{$user->id}_jwt"
  99. : "store_{$user->id}_jwt";
  100. return Cache::delete($cacheKey);
  101. }
  102. /**
  103. * 刷新 Token(续期,thans/jwt 内置支持)
  104. * @param string $oldToken 旧 Token
  105. * @return string|null 新 Token(无效返回null)
  106. */
  107. public function refreshToken(string $oldToken): ?string
  108. {
  109. $oldToken = trim(str_replace('Bearer ', '', $oldToken));
  110. if (empty($oldToken)) {
  111. return null;
  112. }
  113. try {
  114. // 验证旧 Token 并生成新 Token(自动续期)
  115. $newToken = JWT::refresh($oldToken);
  116. // 更新缓存中的 Token
  117. $payload = JWT::verify($oldToken);
  118. if (isset($payload['user_id'])) {
  119. $user = User::find($payload['user_id']);
  120. if ($user) {
  121. $cacheKey = $user->is_store == 0
  122. ? "user_{$user->id}_jwt"
  123. : "store_{$user->id}_jwt";
  124. Cache::set($cacheKey, $newToken, $this->exp);
  125. }
  126. }
  127. return $newToken;
  128. } catch (JWTException $e) {
  129. return null;
  130. }
  131. }
  132. }