相同的设备标识dice只保证一个auth-token生效,清除掉多余的同一个dice多余的auth-token
This commit is contained in:
54
server/app/api/cache/AuthTokenCache.php
vendored
Normal file
54
server/app/api/cache/AuthTokenCache.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\api\cache;
|
||||
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 按设备标识存储当前有效的 auth-token,同一设备只保留最新一个,旧 token 自动失效
|
||||
*/
|
||||
class AuthTokenCache
|
||||
{
|
||||
private static function prefix(): string
|
||||
{
|
||||
return config('api.auth_token_device_prefix', 'api:auth_token:');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该设备当前有效的 auth-token(会覆盖同设备之前的 token,使旧 token 失效)
|
||||
* @param string $device 设备标识,如 dice
|
||||
* @param string $token 完整 auth-token 字符串
|
||||
* @param int $ttl 过期时间(秒),应与 auth_token_exp 一致
|
||||
*/
|
||||
public static function setDeviceToken(string $device, string $token, int $ttl): bool
|
||||
{
|
||||
if ($device === '' || $ttl <= 0) {
|
||||
return false;
|
||||
}
|
||||
$key = self::prefix() . $device;
|
||||
return Cache::set($key, $token, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该设备当前有效的 auth-token,不存在或已过期返回 null
|
||||
*/
|
||||
public static function getDeviceToken(string $device): ?string
|
||||
{
|
||||
if ($device === '') {
|
||||
return null;
|
||||
}
|
||||
$key = self::prefix() . $device;
|
||||
$value = Cache::get($key);
|
||||
return $value !== null && $value !== '' ? (string) $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验请求中的 token 是否为该设备当前唯一有效 token
|
||||
*/
|
||||
public static function isCurrentToken(string $device, string $token): bool
|
||||
{
|
||||
$current = self::getDeviceToken($device);
|
||||
return $current !== null && $current === $token;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use support\Response;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use plugin\saiadmin\basic\OpenController;
|
||||
use app\api\util\ReturnCode;
|
||||
use app\api\cache\AuthTokenCache;
|
||||
|
||||
/**
|
||||
* API 鉴权 Token 接口
|
||||
@@ -57,7 +58,7 @@ class AuthTokenController extends OpenController
|
||||
return $this->fail('签名验证失败', ReturnCode::EMPTY_PARAMS);
|
||||
}
|
||||
|
||||
$exp = config('api.auth_token_exp', 86400);
|
||||
$exp = (int) config('api.auth_token_exp', 86400);
|
||||
$tokenResult = JwtToken::generateToken([
|
||||
'id' => 0,
|
||||
'plat' => 'api',
|
||||
@@ -65,6 +66,9 @@ class AuthTokenController extends OpenController
|
||||
'access_exp' => $exp,
|
||||
]);
|
||||
|
||||
// 同一设备只保留最新 token,覆盖后旧 token 失效
|
||||
AuthTokenCache::setDeviceToken($device, $tokenResult['access_token'], $exp);
|
||||
|
||||
return $this->success([
|
||||
'auth-token' => $tokenResult['access_token'],
|
||||
'expires_in' => $tokenResult['expires_in'],
|
||||
|
||||
@@ -11,6 +11,7 @@ use Tinywan\Jwt\JwtToken;
|
||||
use Tinywan\Jwt\Exception\JwtTokenException;
|
||||
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
|
||||
use app\api\util\ReturnCode;
|
||||
use app\api\cache\AuthTokenCache;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,12 @@ class CheckAuthTokenMiddleware implements MiddlewareInterface
|
||||
throw new ApiException('auth-token 无效(非 API 凭证)', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
// 同一设备只允许一个 auth-token 生效,非当前 token 视为已失效
|
||||
$device = (string) ($extend['device'] ?? '');
|
||||
if ($device !== '' && !AuthTokenCache::isCurrentToken($device, $token)) {
|
||||
throw new ApiException('auth-token 已失效(该设备已签发新凭证,请使用新 auth-token)', ReturnCode::TOKEN_TIMEOUT);
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user