Files
huangjingfen/pro_v3.5.1/app/services/auth/AccessTokenService.php
2026-04-29 17:32:08 +08:00

101 lines
3.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | Author: ScottPan Team
// +----------------------------------------------------------------------
namespace app\services\auth;
use crmeb\exceptions\AuthException;
use crmeb\services\CacheService;
use crmeb\utils\ApiErrorCode;
use crmeb\utils\JwtAuth;
use Firebase\JWT\ExpiredException;
/**
* 本项目自有 access token 服务,不依赖 CRMEB 商业授权基础类。
*/
class AccessTokenService
{
/**
* 创建 access token并写入对应类型的 token bucket。
*/
public function createToken(int $id, string $type, string $authHash, array $extra = []): array
{
/** @var JwtAuth $jwtAuth */
$jwtAuth = app()->make(JwtAuth::class);
return $jwtAuth->createToken($id, $type, $extra + ['auth' => $authHash]);
}
/**
* 解析并校验 access token。
*
* @param callable $resolver 根据 token 内的 id 读取账号模型或数组
* @param callable $authHashResolver 根据账号返回当前有效的 auth hash
* @param callable|null $accountValidator 根据账号判断当前 token 是否仍可用
* @return mixed
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function parseToken(string $token, string $type, callable $resolver, callable $authHashResolver, callable $accountValidator = null)
{
if (!$token || $token === 'undefined') {
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
/** @var JwtAuth $jwtAuth */
$jwtAuth = app()->make(JwtAuth::class);
[$id, $tokenType, $auth] = $jwtAuth->parseToken($token);
if (!$id || $tokenType !== $type) {
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
$md5Token = md5($token);
$cacheToken = CacheService::redisHandler($type)->get($md5Token, null);
if (!$cacheToken) {
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) {
$this->clearToken($md5Token, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
try {
$jwtAuth->verifyToken();
CacheService::setTokenBucket($md5Token, $cacheToken, $cacheToken['exp'] ?? null, $type);
} catch (ExpiredException $e) {
$cacheToken['invalidNum'] = ($cacheToken['invalidNum'] ?? 0) + 1;
CacheService::setTokenBucket($md5Token, $cacheToken, $cacheToken['exp'] ?? null, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN);
} catch (\Throwable $e) {
$this->clearToken($md5Token, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
$account = $resolver($id);
if (!$account) {
$this->clearToken($md5Token, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
if ($accountValidator && !$accountValidator($account)) {
$this->clearToken($md5Token, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
if ($auth !== $authHashResolver($account)) {
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
return $account;
}
protected function clearToken(string $md5Token, string $type): void
{
if (!request()->isCli()) {
CacheService::redisHandler($type)->delete($md5Token);
}
}
}