2025-05-05 09:44:50 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Service\common;
|
|
|
|
|
|
|
|
|
|
|
|
use Firebase\JWT\JWT;
|
|
|
|
|
|
use Firebase\JWT\Key;
|
|
|
|
|
|
use Exception;
|
|
|
|
|
|
|
|
|
|
|
|
class JWTService
|
|
|
|
|
|
{
|
|
|
|
|
|
private static mixed $_instance;
|
|
|
|
|
|
private string $secretKey = 'cc_spa_jwt_secret_key'; // 请替换为你的密钥
|
|
|
|
|
|
|
|
|
|
|
|
private string $token;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取实例
|
|
|
|
|
|
* @return null|static
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function getInstance(): null|static
|
|
|
|
|
|
{
|
|
|
|
|
|
$name = get_called_class();
|
|
|
|
|
|
if (!isset(self::$_instance[$name])) {
|
|
|
|
|
|
self::$_instance[$name] = new static();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return self::$_instance[$name];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取 Token
|
|
|
|
|
|
* @return $this|null
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getToken(): null|static
|
|
|
|
|
|
{
|
|
|
|
|
|
$token = request()->bearerToken();
|
|
|
|
|
|
if (empty($token)) return UtilsService::getInstance()->notAuth('请先登录');
|
|
|
|
|
|
$this->token = $token;
|
|
|
|
|
|
return $this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成 JWT Token
|
|
|
|
|
|
* @param array $data 要嵌入到 token 中的数据
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function generateToken(array $data): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$issuedAt = time();
|
2025-05-05 14:20:17 +08:00
|
|
|
|
$expirationTime = $issuedAt + config('cc.redis.jwt_ttl', 600000);
|
2025-05-05 09:44:50 +08:00
|
|
|
|
|
|
|
|
|
|
$payload = [
|
|
|
|
|
|
'iat' => $issuedAt,
|
|
|
|
|
|
'exp' => $expirationTime,
|
|
|
|
|
|
'data' => $data
|
|
|
|
|
|
];
|
2025-05-07 14:48:34 +08:00
|
|
|
|
RedisService::getInstance()->init(config('cc.redis.jwt'))->set($data['id'], json_encode($data));
|
2025-05-05 09:44:50 +08:00
|
|
|
|
return JWT::encode($payload, $this->secretKey, 'HS256');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析 JWT Token
|
|
|
|
|
|
* @return object|null 返回解码后的 payload 或者 null 如果验证失败
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function parseToken(): ?object
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (empty($this->token)) return UtilsService::getInstance()->notAuth('请先登录');
|
|
|
|
|
|
return JWT::decode($this->token, new Key($this->secretKey, 'HS256'));
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
return UtilsService::getInstance()->notAuth('【1】Token解析失败!请重新登录:'. $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析 JWT Token
|
|
|
|
|
|
* @return array|null 返回解码后的 payload 或者 null 如果验证失败
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getUserInfo(): ?array
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
$jwt = $this->parseToken();
|
2025-05-05 14:20:17 +08:00
|
|
|
|
$user = RedisService::getInstance()->init(config('cc.redis.jwt'))->get($jwt->data->id);
|
2025-05-05 09:44:50 +08:00
|
|
|
|
if (empty($user)) return UtilsService::getInstance()->notAuth('登录状态过期');
|
|
|
|
|
|
return json_decode($user, true);
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
return UtilsService::getInstance()->notAuth('【2】Token解析失败!请重新登录:'. $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 续签 JWT Token
|
|
|
|
|
|
* @return string|null 新的 JWT 字符串或者 null 如果原 token 已过期或无效
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function refreshToken(): ?string
|
|
|
|
|
|
{
|
|
|
|
|
|
$decoded = $this->parseToken();
|
|
|
|
|
|
if ($decoded === null || !property_exists($decoded, 'data')) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $this->generateToken((array)$decoded->data);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|