166940d5a6
Made-with: Cursor
138 lines
3.2 KiB
PHP
138 lines
3.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace app\api\common;
|
|
|
|
/**
|
|
* JWT 工具类
|
|
* 用于生成和验证 JWT Token
|
|
*/
|
|
class Jwt
|
|
{
|
|
/**
|
|
* 生成 Token
|
|
* @param array $payload 载荷数据
|
|
* @return string
|
|
*/
|
|
public static function encode(array $payload): string
|
|
{
|
|
$config = config('jwt');
|
|
|
|
// 添加标准声明
|
|
$payload['iat'] = time();
|
|
$payload['iss'] = $config['issuer'] ?? 'dyai-api';
|
|
$payload['exp'] = time() + ($config['expire'] ?? 604800);
|
|
|
|
// 编码
|
|
$header = self::base64UrlEncode(json_encode(['typ' => 'JWT', 'alg' => 'HS256']));
|
|
$body = self::base64UrlEncode(json_encode($payload));
|
|
|
|
// 签名
|
|
$signature = self::signature("$header.$body", $config['secret'] ?? 'default_secret');
|
|
|
|
return "$header.$body.$signature";
|
|
}
|
|
|
|
/**
|
|
* 解析 Token
|
|
* @param string $token
|
|
* @return array|null
|
|
*/
|
|
public static function decode(string $token): ?array
|
|
{
|
|
$parts = explode('.', $token);
|
|
if (count($parts) !== 3) {
|
|
return null;
|
|
}
|
|
|
|
[$header, $body, $signature] = $parts;
|
|
|
|
// 验证签名
|
|
$config = config('jwt');
|
|
$expectedSignature = self::signature("$header.$body", $config['secret'] ?? 'default_secret');
|
|
|
|
if (!hash_equals($expectedSignature, $signature)) {
|
|
return null;
|
|
}
|
|
|
|
// 解码载荷
|
|
$payload = json_decode(self::base64UrlDecode($body), true);
|
|
if (!$payload) {
|
|
return null;
|
|
}
|
|
|
|
// 验证过期时间
|
|
if (isset($payload['exp']) && $payload['exp'] < time()) {
|
|
return null;
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
/**
|
|
* 生成刷新 Token
|
|
* @param int $userid
|
|
* @return string
|
|
*/
|
|
public static function refreshToken(int $userid): string
|
|
{
|
|
$config = config('jwt');
|
|
|
|
$payload = [
|
|
'userid' => $userid,
|
|
'type' => 'refresh',
|
|
'iat' => time(),
|
|
'exp' => time() + ($config['refresh_expire'] ?? 2592000),
|
|
];
|
|
|
|
return self::encode($payload);
|
|
}
|
|
|
|
/**
|
|
* 从请求头获取 Token
|
|
* @return string|null
|
|
*/
|
|
public static function getTokenFromRequest(): ?string
|
|
{
|
|
$request = request();
|
|
|
|
// 从 Authorization 头获取
|
|
$authorization = $request->header('Authorization', '');
|
|
if (preg_match('/Bearer\s+(.+)/', $authorization, $matches)) {
|
|
return $matches[1];
|
|
}
|
|
|
|
// 从参数获取
|
|
$token = $request->param('token');
|
|
if ($token) {
|
|
return $token;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* URL安全的 Base64 编码
|
|
*/
|
|
private static function base64UrlEncode(string $data): string
|
|
{
|
|
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
|
}
|
|
|
|
/**
|
|
* URL安全的 Base64 解码
|
|
*/
|
|
private static function base64UrlDecode(string $data): string
|
|
{
|
|
return base64_decode(strtr($data, '-_', '+/'));
|
|
}
|
|
|
|
/**
|
|
* 生成签名
|
|
*/
|
|
private static function signature(string $data, string $secret): string
|
|
{
|
|
return self::base64UrlEncode(hash_hmac('sha256', $data, $secret, true));
|
|
}
|
|
}
|