feat(player-auth): add JWT TTL check and AES wrapped token support

1. 新增JWT有效期校验,限制exp-iat最大时长并支持强制校验iat字段
2. 新增AES-GCM密文Token解包能力,支持非标准JWT格式的令牌传递
3. 新增相关配置项和环境变量,可灵活调整校验策略
This commit is contained in:
2026-05-14 09:37:52 +08:00
parent 9d3d086adc
commit c9c1fecfcf
4 changed files with 148 additions and 2 deletions

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Support;
/**
* URL/Query 传入的 AES-256-GCM 密文 Token 解包为内层 JWT 明文字符串。
*
* 约定Base64( IV(12) ciphertext tag(16) ),明文为 UTF-8 JWT三点分段
* 密钥:`config('lottery.player_auth.aes.key_base64')` Base64 解码后为 32 字节。
*/
final class PlayerTokenAesUnwrap
{
/**
* @return 内层 JWT null(未启用/格式错/解密失败)
*/
public static function tryUnwrap(string $opaque): ?string
{
$keyB64 = config('lottery.player_auth.aes.key_base64');
if (! is_string($keyB64) || trim($keyB64) === '') {
return null;
}
$keyRaw = base64_decode(trim($keyB64), true);
if (! is_string($keyRaw) || strlen($keyRaw) !== 32) {
return null;
}
$trim = trim($opaque);
if ($trim === '') {
return null;
}
$bin = base64_decode(strtr($trim, '-_', '+/'), true);
if ($bin === false || strlen($bin) < 12 + 1 + 16) {
return null;
}
$iv = substr($bin, 0, 12);
$tag = substr($bin, -16);
$cipher = substr($bin, 12, -16);
$plain = openssl_decrypt($cipher, 'aes-256-gcm', $keyRaw, OPENSSL_RAW_DATA, $iv, $tag);
if (! is_string($plain) || $plain === '') {
return null;
}
return self::looksLikeJwt($plain) ? $plain : null;
}
private static function looksLikeJwt(string $s): bool
{
return preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $s) === 1;
}
}