Files
dafuweng-saiadmin6.x/server/app/api/cache/UserCache.php

182 lines
5.6 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
declare(strict_types=1);
namespace app\api\cache;
use support\think\Cache;
/**
* API 用户信息 Redis 缓存
* key = base64(user_id)value = 加密后的用户信息 JSON
*/
class UserCache
{
private static function prefix(): string
{
return config('api.user_cache_prefix', 'api:user:');
}
private static function expire(): int
{
return (int) config('api.user_cache_expire', 604800);
}
private static function encryptKey(): string
{
$key = config('api.user_encrypt_key', 'dafuweng_api_user_cache_key_32');
return str_pad($key, 32, '0', STR_PAD_RIGHT);
}
/** 加密 */
public static function encrypt(string $data): string
{
$key = self::encryptKey();
$iv = substr(md5($key), 0, 16);
return base64_encode(openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv));
}
/** 解密 */
public static function decrypt(string $data): string
{
$key = self::encryptKey();
$iv = substr(md5($key), 0, 16);
$dec = openssl_decrypt(base64_decode($data, true), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return $dec !== false ? $dec : '';
}
/**
* 写入用户信息到 Redis
* @param int $userId
* @param array $userInfo 从数据库读取的用户信息(可含敏感字段,会加密存储)
*/
public static function setUser(int $userId, array $userInfo): bool
{
$key = self::prefix() . base64_encode((string) $userId);
$value = self::encrypt(json_encode($userInfo));
return Cache::set($key, $value, self::expire());
}
/**
* 从 Redis 读取用户信息
* @return array 解密后的用户信息,不存在或失败返回空数组
*/
public static function getUser(int $userId): array
{
$key = self::prefix() . base64_encode((string) $userId);
$value = Cache::get($key);
if ($value === null || $value === '') {
return [];
}
$dec = self::decrypt($value);
if ($dec === '') {
return [];
}
$data = json_decode($dec, true);
return is_array($data) ? $data : [];
}
/**
* 仅从缓存读取用户平台币 coin不查库低延迟
* @return int|float|null 余额,缓存未命中返回 null缓存中 coin 可能为字符串,统一转为数值)
*/
public static function getUserCoin(int $userId): int|float|null
{
$user = self::getUser($userId);
if (empty($user) || !array_key_exists('coin', $user)) {
return null;
}
$coin = $user['coin'];
if (is_int($coin) || is_float($coin)) {
return $coin;
}
if (is_string($coin) && is_numeric($coin)) {
return str_contains($coin, '.') ? (float) $coin : (int) $coin;
}
return null;
}
/** 删除用户缓存 */
public static function deleteUser(int $userId): bool
{
$key = self::prefix() . base64_encode((string) $userId);
return Cache::delete($key);
}
/** user-token 黑名单前缀(退出登录后使 token 失效) */
private static function blacklistPrefix(): string
{
return config('api.user_cache_prefix', 'api:user:') . 'token_blacklist:';
}
/**
* 将 user-token 加入黑名单(退出登录)
* @param string $token 完整 token 字符串
* @param int $ttl 黑名单过期时间(秒),建议为 token 剩余有效期
*/
public static function addTokenToBlacklist(string $token, int $ttl = 86400): bool
{
if ($ttl <= 0) {
return true;
}
$key = self::blacklistPrefix() . md5($token);
return Cache::set($key, '1', $ttl);
}
/**
* 检查 user-token 是否在黑名单中(已退出)
*/
public static function isTokenBlacklisted(string $token): bool
{
$key = self::blacklistPrefix() . md5($token);
$val = Cache::get($key);
return $val !== null && $val !== '';
}
/** 当前有效 user-token 按用户存储的 key 前缀(重新登录/注册后覆盖,保证单用户单 token */
private static function currentTokenPrefix(): string
{
return config('api.user_token_current_prefix', 'api:user:current_token:');
}
private static function userTokenExpire(): int
{
return (int) config('api.user_token_exp', 604800);
}
/**
* 设置该用户当前唯一有效的 user-token登录/注册时调用,会覆盖该用户之前的 token
* @param int $userId 用户 ID
* @param string $token 完整 user-token 字符串
*/
public static function setCurrentUserToken(int $userId, string $token): bool
{
if ($userId <= 0 || $token === '') {
return false;
}
$key = self::currentTokenPrefix() . $userId;
return Cache::set($key, $token, self::userTokenExpire());
}
/**
* 获取该用户当前在服务端登记的有效 user-token不存在或已过期返回 null
*/
public static function getCurrentUserToken(int $userId): ?string
{
if ($userId <= 0) {
return null;
}
$key = self::currentTokenPrefix() . $userId;
$value = Cache::get($key);
return $value !== null && $value !== '' ? (string) $value : null;
}
/**
* 校验请求中的 token 是否为该用户当前唯一有效 token
*/
public static function isCurrentUserToken(int $userId, string $token): bool
{
$current = self::getCurrentUserToken($userId);
return $current !== null && $current === $token;
}
}