1212
1313use support \Redis ;
1414use Tinywan \Jwt \Exception \JwtCacheTokenException ;
15+ use Tinywan \Jwt \Exception \RedisConnectionException ;
16+ use RedisException ;
1517
1618class RedisHandler
1719{
20+ /**
21+ * @desc: 检查Redis连接状态
22+ * @throws RedisConnectionException 当Redis连接失败时抛出异常
23+ */
24+ private static function checkConnection ()
25+ {
26+ try {
27+ if (!Redis::ping ()) {
28+ throw new RedisConnectionException ('Redis连接不可用 ' );
29+ }
30+ } catch (RedisException $ e ) {
31+ throw new RedisConnectionException ('Redis连接失败: ' . $ e ->getMessage ());
32+ }
33+ }
34+
35+ /**
36+ * @desc: 生成缓存键名
37+ * @param string $pre 键前缀
38+ * @param string $client 客户端类型
39+ * @param string $uid 用户ID
40+ * @return string 完整的缓存键名
41+ */
42+ private static function generateKey (string $ pre , string $ client , string $ uid )
43+ {
44+ return sprintf ('%s%s:%s ' , $ pre , $ client , $ uid );
45+ }
46+
47+ /**
48+ * @desc: 安全执行Redis操作
49+ * @param callable $callback Redis操作回调函数
50+ * @return mixed 回调函数执行结果
51+ * @throws RedisConnectionException
52+ */
53+ private static function safeExecute (callable $ callback )
54+ {
55+ self ::checkConnection ();
56+
57+ try {
58+ return $ callback ();
59+ } catch (RedisException $ e ) {
60+ throw new RedisConnectionException ('Redis操作失败: ' . $ e ->getMessage ());
61+ }
62+ }
1863 /**
1964 * @desc: 生成缓存令牌
2065 * (1)登录时,判断该账号是否在其它设备登录,如果有,就请空之前key清除,
@@ -24,13 +69,22 @@ class RedisHandler
2469 * @param string $uid
2570 * @param int $ttl
2671 * @param string $token
72+ * @throws RedisConnectionException
2773 * @author Tinywan(ShaoBo Wan)
2874 */
29- public static function generateToken (string $ pre , string $ client , string $ uid , int $ ttl , string $ token ): void
75+ public static function generateToken (string $ pre , string $ client , string $ uid , int $ ttl , string $ token )
3076 {
31- $ cacheKey = $ pre . $ client . ': ' . $ uid ;
32- Redis::del ($ cacheKey );
33- Redis::setex ($ cacheKey , $ ttl , $ token );
77+ self ::validateRedisParams ($ ttl , $ token );
78+ $ cacheKey = self ::generateKey ($ pre , $ client , $ uid );
79+
80+ self ::safeExecute (function () use ($ cacheKey , $ ttl , $ token ) {
81+ Redis::del ($ cacheKey );
82+ $ result = Redis::setex ($ cacheKey , $ ttl , $ token );
83+
84+ if (!$ result ) {
85+ throw new RedisConnectionException ('Redis设置令牌失败 ' );
86+ }
87+ });
3488 }
3589
3690
@@ -41,16 +95,28 @@ public static function generateToken(string $pre, string $client, string $uid, i
4195 * @param string $uid
4296 * @param int $ttl
4397 * @param string $token
98+ * @throws RedisConnectionException
4499 * @return void
45100 */
46- public static function refreshToken (string $ pre , string $ client , string $ uid , int $ ttl , string $ token ): void
101+ public static function refreshToken (string $ pre , string $ client , string $ uid , int $ ttl , string $ token )
47102 {
48- $ cacheKey = $ pre . $ client . ': ' . $ uid ;
49- $ isExists = Redis::exists ($ cacheKey );
50- if ($ isExists ) {
51- $ ttl = Redis::ttl ($ cacheKey );
52- }
53- Redis::setex ($ cacheKey , $ ttl , $ token );
103+ self ::validateRedisParams ($ ttl , $ token );
104+ $ cacheKey = self ::generateKey ($ pre , $ client , $ uid );
105+
106+ self ::safeExecute (function () use ($ cacheKey , $ ttl , $ token ) {
107+ $ isExists = Redis::exists ($ cacheKey );
108+ if ($ isExists ) {
109+ $ currentTtl = Redis::ttl ($ cacheKey );
110+ if ($ currentTtl > 0 ) {
111+ $ ttl = $ currentTtl ;
112+ }
113+ }
114+
115+ $ result = Redis::setex ($ cacheKey , $ ttl , $ token );
116+ if (!$ result ) {
117+ throw new RedisConnectionException ('Redis刷新令牌失败 ' );
118+ }
119+ });
54120 }
55121
56122 /**
@@ -60,18 +126,34 @@ public static function refreshToken(string $pre, string $client, string $uid, in
60126 * @param string $uid
61127 * @param string $token
62128 * @return bool
129+ * @throws RedisConnectionException
130+ * @throws JwtCacheTokenException
63131 * @author Tinywan(ShaoBo Wan)
64132 */
65133 public static function verifyToken (string $ pre , string $ client , string $ uid , string $ token ): bool
66134 {
67- $ cacheKey = $ pre . $ client . ': ' . $ uid ;
68- if (!Redis::exists ($ cacheKey )) {
69- throw new JwtCacheTokenException ('身份验证会话已过期,请再次登录! ' );
70- }
71- if (Redis::get ($ cacheKey ) != $ token ) {
72- throw new JwtCacheTokenException ('该账号已在其他设备登录,强制下线 ' );
135+ if (empty ($ token )) {
136+ throw new \InvalidArgumentException ('Token不能为空 ' );
73137 }
74- return true ;
138+
139+ $ cacheKey = self ::generateKey ($ pre , $ client , $ uid );
140+
141+ return self ::safeExecute (function () use ($ cacheKey , $ token ) {
142+ if (!Redis::exists ($ cacheKey )) {
143+ throw new JwtCacheTokenException ('身份验证会话已过期,请再次登录! ' );
144+ }
145+
146+ $ cachedToken = Redis::get ($ cacheKey );
147+ if ($ cachedToken === false ) {
148+ throw new RedisConnectionException ('Redis获取令牌失败 ' );
149+ }
150+
151+ if ($ cachedToken != $ token ) {
152+ throw new JwtCacheTokenException ('该账号已在其他设备登录,强制下线 ' );
153+ }
154+
155+ return true ;
156+ });
75157 }
76158
77159 /**
@@ -80,11 +162,49 @@ public static function verifyToken(string $pre, string $client, string $uid, str
80162 * @param string $client
81163 * @param string $uid
82164 * @return bool
165+ * @throws RedisConnectionException
83166 * @author Tinywan(ShaoBo Wan)
84167 */
85168 public static function clearToken (string $ pre , string $ client , string $ uid ): bool
86169 {
87- Redis::del ($ pre . $ client . ': ' . $ uid );
88- return true ;
170+ $ cacheKey = self ::generateKey ($ pre , $ client , $ uid );
171+
172+ return self ::safeExecute (function () use ($ cacheKey ) {
173+ $ result = Redis::del ($ cacheKey );
174+ if ($ result === false ) {
175+ throw new RedisConnectionException ('Redis清理令牌失败 ' );
176+ }
177+ return true ;
178+ });
179+ }
180+
181+ /**
182+ * @desc: 验证Redis操作参数
183+ * @param int $ttl 过期时间
184+ * @param string $token 令牌
185+ * @throws \InvalidArgumentException
186+ */
187+ private static function validateRedisParams (int $ ttl , string $ token ): void
188+ {
189+ if ($ ttl <= 0 ) {
190+ throw new \InvalidArgumentException ('TTL必须大于0 ' );
191+ }
192+
193+ if (empty ($ token )) {
194+ throw new \InvalidArgumentException ('Token不能为空 ' );
195+ }
196+ }
197+
198+ /**
199+ * @desc: 检查Redis是否可用
200+ * @return bool Redis是否可用
201+ */
202+ public static function isAvailable (): bool
203+ {
204+ try {
205+ return Redis::ping () === true ;
206+ } catch (RedisException $ e ) {
207+ return false ;
208+ }
89209 }
90210}
0 commit comments