Commit aa3d7e969916d82e9ad37be4d7e42fa7dbf44db5
1 parent
6947996f
feat:权限控制
Showing
15 changed files
with
1813 additions
and
110 deletions
src/main/java/com/huaheng/common/constant/ShiroRedisConstants.java
0 → 100644
src/main/java/com/huaheng/common/exception/user/UserPasswordRetryLimitCountException.java
... | ... | @@ -2,15 +2,18 @@ package com.huaheng.common.exception.user; |
2 | 2 | |
3 | 3 | /** |
4 | 4 | * 用户错误记数异常类 |
5 | - * | |
5 | + * | |
6 | 6 | * @author huaheng |
7 | 7 | */ |
8 | -public class UserPasswordRetryLimitCountException extends UserException | |
9 | -{ | |
8 | +public class UserPasswordRetryLimitCountException extends UserException { | |
10 | 9 | private static final long serialVersionUID = 1L; |
11 | 10 | |
12 | - public UserPasswordRetryLimitCountException(int retryLimitCount, String password) | |
13 | - { | |
14 | - super("user.password.retry.limit.count", new Object[] { retryLimitCount, password }); | |
11 | + public UserPasswordRetryLimitCountException(int retryLimitCount, String password) { | |
12 | + super("user.password.retry.limit.count", new Object[]{retryLimitCount, password}); | |
13 | + } | |
14 | + | |
15 | + | |
16 | + public UserPasswordRetryLimitCountException(int retryLimitCount) { | |
17 | + super("user.password.retry.limit.count", new Object[]{retryLimitCount}); | |
15 | 18 | } |
16 | 19 | } |
... | ... |
src/main/java/com/huaheng/common/utils/RedisUtils.java
0 → 100644
1 | +package com.huaheng.common.utils; | |
2 | + | |
3 | +import com.huaheng.common.constant.ShiroRedisConstants; | |
4 | +import org.apache.shiro.dao.DataAccessException; | |
5 | +import org.springframework.beans.factory.annotation.Autowired; | |
6 | +import org.springframework.data.redis.connection.RedisConnection; | |
7 | +import org.springframework.data.redis.core.Cursor; | |
8 | +import org.springframework.data.redis.core.RedisCallback; | |
9 | +import org.springframework.data.redis.core.RedisTemplate; | |
10 | +import org.springframework.data.redis.core.ScanOptions; | |
11 | +import org.springframework.stereotype.Component; | |
12 | +import org.springframework.util.CollectionUtils; | |
13 | + | |
14 | +import java.util.*; | |
15 | +import java.util.concurrent.TimeUnit; | |
16 | + | |
17 | +@Component | |
18 | +public class RedisUtils { | |
19 | + /** | |
20 | + * 注入自定义redisTemplate bean | |
21 | + */ | |
22 | + @Autowired | |
23 | + private RedisTemplate<String, Object> redisTemplate; | |
24 | + | |
25 | + @Autowired | |
26 | + private RedisTemplate<String, Object> shiroRedisTemplate; | |
27 | + | |
28 | + public static String getRedisSessionKey(String SessionId) { | |
29 | + return ShiroRedisConstants.keyPrefix + SessionId; | |
30 | + } | |
31 | + /* | |
32 | + * @Autowired private StringRedisTemplate stringRedisTemplate; | |
33 | + */ | |
34 | + | |
35 | + /** | |
36 | + * 指定缓存失效时间 | |
37 | + * | |
38 | + * @param isShiroRedis 是否属于shiroredis缓存 | |
39 | + * @param key 键 | |
40 | + * @param time 时间(秒) | |
41 | + * @return | |
42 | + */ | |
43 | + public boolean expire(Boolean isShiroRedis, String key, long time) { | |
44 | + try { | |
45 | + if (isShiroRedis) { | |
46 | + if (time > 0) { | |
47 | + | |
48 | + shiroRedisTemplate.expire(key, time, TimeUnit.MILLISECONDS); | |
49 | + | |
50 | + } | |
51 | + } else { | |
52 | + if (time > 0) { | |
53 | + | |
54 | + redisTemplate.expire(key, time, TimeUnit.MILLISECONDS); | |
55 | + | |
56 | + } | |
57 | + } | |
58 | + | |
59 | + return true; | |
60 | + } catch (Exception e) { | |
61 | + e.printStackTrace(); | |
62 | + return false; | |
63 | + } | |
64 | + } | |
65 | + | |
66 | + /** | |
67 | + * 根据key获取过期时间 | |
68 | + * | |
69 | + * @param isShiroRedis shiroRedisCache | |
70 | + * @param key 键 不能为null | |
71 | + * @return 时间(秒) 返回0代表为永久有效 | |
72 | + */ | |
73 | + public long getExpire(Boolean isShiroRedis, String key) { | |
74 | + return isShiroRedis ? shiroRedisTemplate.getExpire(key, TimeUnit.MILLISECONDS) | |
75 | + : redisTemplate.getExpire(key, TimeUnit.MILLISECONDS); | |
76 | + } | |
77 | + | |
78 | + /** | |
79 | + * 判断key是否存在 | |
80 | + * | |
81 | + * @param isShiroRedis shiroRedisCache | |
82 | + * @param key 键 | |
83 | + * @return true 存在 false不存在 | |
84 | + */ | |
85 | + public boolean hasKey(Boolean isShiroRedis, String key) { | |
86 | + try { | |
87 | + return isShiroRedis ? shiroRedisTemplate.hasKey(key) : redisTemplate.hasKey(key); | |
88 | + } catch (Exception e) { | |
89 | + e.printStackTrace(); | |
90 | + return false; | |
91 | + } | |
92 | + } | |
93 | + | |
94 | + /** | |
95 | + * 删除缓存 | |
96 | + * | |
97 | + * @param key 可以传一个值 或多个 | |
98 | + */ | |
99 | + @SuppressWarnings("unchecked") | |
100 | + public void del(Boolean isShiroRedis, String... key) { | |
101 | + if (isShiroRedis) { | |
102 | + if (key != null && key.length > 0) { | |
103 | + if (key.length == 1) { | |
104 | + shiroRedisTemplate.delete(key[0]); | |
105 | + } else { | |
106 | + shiroRedisTemplate.delete(CollectionUtils.arrayToList(key)); | |
107 | + } | |
108 | + } | |
109 | + } else { | |
110 | + | |
111 | + if (key != null && key.length > 0) { | |
112 | + if (key.length == 1) { | |
113 | + redisTemplate.delete(key[0]); | |
114 | + } else { | |
115 | + redisTemplate.delete(CollectionUtils.arrayToList(key)); | |
116 | + } | |
117 | + } | |
118 | + } | |
119 | + | |
120 | + } | |
121 | + | |
122 | + /** | |
123 | + * 批量删除key | |
124 | + * | |
125 | + * @param keys | |
126 | + */ | |
127 | + @SuppressWarnings({"rawtypes", "unchecked"}) | |
128 | + public void del(Boolean isShiroRedis, Collection keys) { | |
129 | + | |
130 | + if (isShiroRedis) { | |
131 | + shiroRedisTemplate.delete(keys); | |
132 | + } else { | |
133 | + redisTemplate.delete(keys); | |
134 | + } | |
135 | + | |
136 | + } | |
137 | + | |
138 | + /** | |
139 | + * 使用scan命令 查询某些前缀的key | |
140 | + * | |
141 | + * @param key | |
142 | + * @return | |
143 | + */ | |
144 | + public Set<String> scan(Boolean isShiroRedis, String key) { | |
145 | + if (isShiroRedis) { | |
146 | + Set<String> execute = this.shiroRedisTemplate.execute(new RedisCallback<Set<String>>() { | |
147 | + | |
148 | + @Override | |
149 | + public Set<String> doInRedis(RedisConnection connection) throws DataAccessException { | |
150 | + | |
151 | + Set<String> binaryKeys = new HashSet<>(); | |
152 | + | |
153 | + Cursor<byte[]> cursor = connection | |
154 | + .scan(new ScanOptions.ScanOptionsBuilder().match(key).count(1000).build()); | |
155 | + while (cursor.hasNext()) { | |
156 | + binaryKeys.add(new String(cursor.next())); | |
157 | + } | |
158 | + return binaryKeys; | |
159 | + } | |
160 | + }); | |
161 | + return execute; | |
162 | + } else { | |
163 | + | |
164 | + Set<String> execute = this.redisTemplate.execute(new RedisCallback<Set<String>>() { | |
165 | + | |
166 | + @Override | |
167 | + public Set<String> doInRedis(RedisConnection connection) throws DataAccessException { | |
168 | + | |
169 | + Set<String> binaryKeys = new HashSet<>(); | |
170 | + | |
171 | + Cursor<byte[]> cursor = connection | |
172 | + .scan(new ScanOptions.ScanOptionsBuilder().match(key).count(1000).build()); | |
173 | + while (cursor.hasNext()) { | |
174 | + binaryKeys.add(new String(cursor.next())); | |
175 | + } | |
176 | + return binaryKeys; | |
177 | + } | |
178 | + }); | |
179 | + return execute; | |
180 | + } | |
181 | + | |
182 | + } | |
183 | + | |
184 | + /** | |
185 | + * 使用scan命令 查询某些前缀的key 有多少个 可用来获取当前session数量,也就是在线用户 | |
186 | + * | |
187 | + * @param key | |
188 | + * @return | |
189 | + */ | |
190 | + public Long scanSize(Boolean isShiroRedis, String key) { | |
191 | + if (isShiroRedis) { | |
192 | + long dbSize = this.shiroRedisTemplate.execute(new RedisCallback<Long>() { | |
193 | + | |
194 | + @Override | |
195 | + public Long doInRedis(RedisConnection connection) throws DataAccessException { | |
196 | + long count = 0L; | |
197 | + Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(1000).build()); | |
198 | + while (cursor.hasNext()) { | |
199 | + cursor.next(); | |
200 | + count++; | |
201 | + } | |
202 | + return count; | |
203 | + } | |
204 | + }); | |
205 | + return dbSize; | |
206 | + } else { | |
207 | + long dbSize = this.redisTemplate.execute(new RedisCallback<Long>() { | |
208 | + | |
209 | + @Override | |
210 | + public Long doInRedis(RedisConnection connection) throws DataAccessException { | |
211 | + long count = 0L; | |
212 | + Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(1000).build()); | |
213 | + while (cursor.hasNext()) { | |
214 | + cursor.next(); | |
215 | + count++; | |
216 | + } | |
217 | + return count; | |
218 | + } | |
219 | + }); | |
220 | + return dbSize; | |
221 | + } | |
222 | + } | |
223 | + | |
224 | + // ============================String(字符串)============================= | |
225 | + | |
226 | + /** | |
227 | + * 普通缓存获取 | |
228 | + * | |
229 | + * @param key 键 | |
230 | + * @return 值 | |
231 | + */ | |
232 | + public Object get(Boolean isShiroRedis, String key) { | |
233 | + return isShiroRedis ? (key == null ? null : shiroRedisTemplate.opsForValue().get(key)) | |
234 | + : (key == null ? null : redisTemplate.opsForValue().get(key)); | |
235 | + } | |
236 | + | |
237 | + /** | |
238 | + * 普通缓存放入 | |
239 | + * | |
240 | + * @param key 键 | |
241 | + * @param value 值 | |
242 | + * @return true成功 false失败 | |
243 | + */ | |
244 | + public boolean set(Boolean isShiroRedis, String key, Object value) { | |
245 | + try { | |
246 | + | |
247 | + if (isShiroRedis) { | |
248 | + | |
249 | + shiroRedisTemplate.opsForValue().set(key, value); | |
250 | + } else { | |
251 | + redisTemplate.opsForValue().set(key, value); | |
252 | + } | |
253 | + | |
254 | + return true; | |
255 | + } catch (Exception e) { | |
256 | + e.printStackTrace(); | |
257 | + return false; | |
258 | + } | |
259 | + } | |
260 | + | |
261 | + /** | |
262 | + * 普通缓存放入并设置时间 | |
263 | + * | |
264 | + * @param key 键 | |
265 | + * @param value 值 | |
266 | + * @param time 时间(分钟) time要大于0 如果time小于等于0 将设置无限期 | |
267 | + * @return true成功 false 失败 | |
268 | + */ | |
269 | + public boolean set(Boolean isShiroRedis, String key, Object value, long time) { | |
270 | + try { | |
271 | + if (isShiroRedis) { | |
272 | + if (time > 0) { | |
273 | + shiroRedisTemplate.opsForValue().set(key, value, time, TimeUnit.MILLISECONDS); | |
274 | + } else { | |
275 | + set(isShiroRedis, key, value); | |
276 | + } | |
277 | + } else { | |
278 | + | |
279 | + if (time > 0) { | |
280 | + redisTemplate.opsForValue().set(key, value, time, TimeUnit.MILLISECONDS); | |
281 | + } else { | |
282 | + set(isShiroRedis, key, value); | |
283 | + } | |
284 | + } | |
285 | + return true; | |
286 | + } catch (Exception e) { | |
287 | + e.printStackTrace(); | |
288 | + return false; | |
289 | + } | |
290 | + } | |
291 | + | |
292 | + /** | |
293 | + * 递增 | |
294 | + * | |
295 | + * @param key 键 | |
296 | + * @param delta 要增加几(大于0) | |
297 | + * @return | |
298 | + */ | |
299 | + public long incr(Boolean isShiroRedis, String key, long delta) { | |
300 | + if (delta < 0) { | |
301 | + throw new RuntimeException("递增因子必须大于0"); | |
302 | + } | |
303 | + return isShiroRedis ? shiroRedisTemplate.opsForValue().increment(key, delta) | |
304 | + : redisTemplate.opsForValue().increment(key, delta); | |
305 | + } | |
306 | + | |
307 | + /** | |
308 | + * 递减 | |
309 | + * | |
310 | + * @param key 键 | |
311 | + * @param delta 要减少几(小于0) | |
312 | + * @return | |
313 | + */ | |
314 | + public long decr(Boolean isShiroRedis, String key, long delta) { | |
315 | + if (delta < 0) { | |
316 | + throw new RuntimeException("递减因子必须大于0"); | |
317 | + } | |
318 | + return isShiroRedis ? shiroRedisTemplate.opsForValue().increment(key, -delta) | |
319 | + : redisTemplate.opsForValue().increment(key, -delta); | |
320 | + } | |
321 | + // ================================Hash(哈希)================================= | |
322 | + | |
323 | + /** | |
324 | + * HashGet | |
325 | + * | |
326 | + * @param key 键 不能为null | |
327 | + * @param item 项 不能为null | |
328 | + * @return 值 | |
329 | + */ | |
330 | + public Object hget(Boolean isShiroRedis, String key, String item) { | |
331 | + return isShiroRedis ? shiroRedisTemplate.opsForHash().get(key, item) | |
332 | + : redisTemplate.opsForHash().get(key, item); | |
333 | + } | |
334 | + | |
335 | + /** | |
336 | + * 获取hashKey对应的所有键值 | |
337 | + * | |
338 | + * @param key 键 | |
339 | + * @return 对应的多个键值 | |
340 | + */ | |
341 | + public Map<Object, Object> hmget(Boolean isShiroRedis, String key) { | |
342 | + return isShiroRedis ? shiroRedisTemplate.opsForHash().entries(key) : redisTemplate.opsForHash().entries(key); | |
343 | + } | |
344 | + | |
345 | + /** | |
346 | + * HashSet | |
347 | + * | |
348 | + * @param key 键 | |
349 | + * @param map 对应多个键值 | |
350 | + * @return true 成功 false 失败 | |
351 | + */ | |
352 | + public boolean hmset(Boolean isShiroRedis, String key, Map<String, Object> map) { | |
353 | + try { | |
354 | + | |
355 | + if (isShiroRedis) { | |
356 | + | |
357 | + shiroRedisTemplate.opsForHash().putAll(key, map); | |
358 | + } else { | |
359 | + redisTemplate.opsForHash().putAll(key, map); | |
360 | + } | |
361 | + | |
362 | + return true; | |
363 | + } catch (Exception e) { | |
364 | + e.printStackTrace(); | |
365 | + return false; | |
366 | + } | |
367 | + } | |
368 | + | |
369 | + /** | |
370 | + * HashSet 并设置时间 | |
371 | + * | |
372 | + * @param isShiroRedis 是否为shiroredis缓存 | |
373 | + * @param key 键 | |
374 | + * @param map 对应多个键值 | |
375 | + * @param time 时间(秒) | |
376 | + * @return true成功 false失败 | |
377 | + */ | |
378 | + public boolean hmset(Boolean isShiroRedis, String key, Map<String, Object> map, long time) { | |
379 | + try { | |
380 | + if (isShiroRedis) { | |
381 | + shiroRedisTemplate.opsForHash().putAll(key, map); | |
382 | + if (time > 0) { | |
383 | + expire(isShiroRedis, key, time); | |
384 | + } | |
385 | + } else { | |
386 | + redisTemplate.opsForHash().putAll(key, map); | |
387 | + if (time > 0) { | |
388 | + expire(isShiroRedis, key, time); | |
389 | + } | |
390 | + } | |
391 | + | |
392 | + return true; | |
393 | + } catch (Exception e) { | |
394 | + e.printStackTrace(); | |
395 | + return false; | |
396 | + } | |
397 | + } | |
398 | + | |
399 | + /** | |
400 | + * 向一张hash表中放入数据,如果不存在将创建 | |
401 | + * | |
402 | + * @param key 键 | |
403 | + * @param item 项 | |
404 | + * @param value 值 | |
405 | + * @return true 成功 false失败 | |
406 | + */ | |
407 | + public boolean hset(Boolean isShiroRedis, String key, String item, Object value) { | |
408 | + try { | |
409 | + if (isShiroRedis) { | |
410 | + shiroRedisTemplate.opsForHash().put(key, item, value); | |
411 | + } else { | |
412 | + redisTemplate.opsForHash().put(key, item, value); | |
413 | + } | |
414 | + | |
415 | + return true; | |
416 | + } catch (Exception e) { | |
417 | + e.printStackTrace(); | |
418 | + return false; | |
419 | + } | |
420 | + } | |
421 | + | |
422 | + /** | |
423 | + * 向一张hash表中放入数据,如果不存在将创建 | |
424 | + * | |
425 | + * @param | |
426 | + * @param key 键 | |
427 | + * @param item 项 | |
428 | + * @param value 值 | |
429 | + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 | |
430 | + * @return true 成功 false失败 | |
431 | + */ | |
432 | + public boolean hset(Boolean isShiroRedis, String key, String item, Object value, long time) { | |
433 | + try { | |
434 | + if (isShiroRedis) { | |
435 | + shiroRedisTemplate.opsForHash().put(key, item, value); | |
436 | + if (time > 0) { | |
437 | + expire(isShiroRedis, key, time); | |
438 | + } | |
439 | + } else { | |
440 | + redisTemplate.opsForHash().put(key, item, value); | |
441 | + if (time > 0) { | |
442 | + expire(isShiroRedis, key, time); | |
443 | + } | |
444 | + } | |
445 | + | |
446 | + return true; | |
447 | + } catch (Exception e) { | |
448 | + e.printStackTrace(); | |
449 | + return false; | |
450 | + } | |
451 | + } | |
452 | + | |
453 | + /** | |
454 | + * 删除hash表中的值 | |
455 | + * | |
456 | + * @param key 键 不能为null | |
457 | + * @param item 项 可以使多个 不能为null | |
458 | + */ | |
459 | + public void hdel(Boolean isShiroRedis, String key, Object... item) { | |
460 | + | |
461 | + if (isShiroRedis) { | |
462 | + shiroRedisTemplate.opsForHash().delete(key, item); | |
463 | + } else { | |
464 | + redisTemplate.opsForHash().delete(key, item); | |
465 | + } | |
466 | + | |
467 | + } | |
468 | + | |
469 | + /** | |
470 | + * 判断hash表中是否有该项的值 | |
471 | + * | |
472 | + * @param key 键 不能为null | |
473 | + * @param item 项 不能为null | |
474 | + * @return true 存在 false不存在 | |
475 | + */ | |
476 | + public boolean hHasKey(String key, String item) { | |
477 | + return redisTemplate.opsForHash().hasKey(key, item); | |
478 | + } | |
479 | + | |
480 | + /** | |
481 | + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 | |
482 | + * | |
483 | + * @param key 键 | |
484 | + * @param item 项 | |
485 | + * @param by 要增加几(大于0) | |
486 | + * @return | |
487 | + */ | |
488 | + public double hincr(String key, String item, double by) { | |
489 | + return redisTemplate.opsForHash().increment(key, item, by); | |
490 | + } | |
491 | + | |
492 | + /** | |
493 | + * hash递减 | |
494 | + * | |
495 | + * @param key 键 | |
496 | + * @param item 项 | |
497 | + * @param by 要减少记(小于0) | |
498 | + * @return | |
499 | + */ | |
500 | + public double hdecr(String key, String item, double by) { | |
501 | + return redisTemplate.opsForHash().increment(key, item, -by); | |
502 | + } | |
503 | + // ============================Set(集合)============================= | |
504 | + | |
505 | + /** | |
506 | + * 根据key获取Set中的所有值 | |
507 | + * | |
508 | + * @param key 键 | |
509 | + * @return | |
510 | + */ | |
511 | + public Set<Object> sGet(String key) { | |
512 | + try { | |
513 | + return redisTemplate.opsForSet().members(key); | |
514 | + } catch (Exception e) { | |
515 | + e.printStackTrace(); | |
516 | + return null; | |
517 | + } | |
518 | + } | |
519 | + | |
520 | + /** | |
521 | + * 根据value从一个set中查询,是否存在 | |
522 | + * | |
523 | + * @param key 键 | |
524 | + * @param value 值 | |
525 | + * @return true 存在 false不存在 | |
526 | + */ | |
527 | + public boolean sHasKey(String key, Object value) { | |
528 | + try { | |
529 | + return redisTemplate.opsForSet().isMember(key, value); | |
530 | + } catch (Exception e) { | |
531 | + e.printStackTrace(); | |
532 | + return false; | |
533 | + } | |
534 | + } | |
535 | + | |
536 | + /** | |
537 | + * 将数据放入set缓存 | |
538 | + * | |
539 | + * @param key 键 | |
540 | + * @param values 值 可以是多个 | |
541 | + * @return 成功个数 | |
542 | + */ | |
543 | + public long sSet(String key, Object... values) { | |
544 | + try { | |
545 | + return redisTemplate.opsForSet().add(key, values); | |
546 | + } catch (Exception e) { | |
547 | + e.printStackTrace(); | |
548 | + return 0; | |
549 | + } | |
550 | + } | |
551 | + | |
552 | + /** | |
553 | + * 将set数据放入缓存 | |
554 | + * | |
555 | + * @param key 键 | |
556 | + * @param time 时间(秒) | |
557 | + * @param values 值 可以是多个 | |
558 | + * @return 成功个数 | |
559 | + */ | |
560 | + public long sSetAndTime(Boolean isShiroRedis, String key, long time, Object... values) { | |
561 | + try { | |
562 | + | |
563 | + if (isShiroRedis) { | |
564 | + Long count = shiroRedisTemplate.opsForSet().add(key, values); | |
565 | + if (time > 0) { | |
566 | + expire(isShiroRedis, key, time); | |
567 | + } | |
568 | + return count; | |
569 | + } else { | |
570 | + Long count = redisTemplate.opsForSet().add(key, values); | |
571 | + if (time > 0) { | |
572 | + expire(isShiroRedis, key, time); | |
573 | + } | |
574 | + return count; | |
575 | + } | |
576 | + | |
577 | + } catch (Exception e) { | |
578 | + e.printStackTrace(); | |
579 | + return 0; | |
580 | + } | |
581 | + } | |
582 | + | |
583 | + /** | |
584 | + * 获取set缓存的长度 | |
585 | + * | |
586 | + * @param key 键 | |
587 | + * @return | |
588 | + */ | |
589 | + public long sGetSetSize(String key) { | |
590 | + try { | |
591 | + return redisTemplate.opsForSet().size(key); | |
592 | + } catch (Exception e) { | |
593 | + e.printStackTrace(); | |
594 | + return 0; | |
595 | + } | |
596 | + } | |
597 | + | |
598 | + /** | |
599 | + * 移除值为value的 | |
600 | + * | |
601 | + * @param key 键 | |
602 | + * @param values 值 可以是多个 | |
603 | + * @return 移除的个数 | |
604 | + */ | |
605 | + public long setRemove(String key, Object... values) { | |
606 | + try { | |
607 | + Long count = redisTemplate.opsForSet().remove(key, values); | |
608 | + return count; | |
609 | + } catch (Exception e) { | |
610 | + e.printStackTrace(); | |
611 | + return 0; | |
612 | + } | |
613 | + } | |
614 | + // ===============================List(列表)================================= | |
615 | + | |
616 | + /** | |
617 | + * 获取list缓存的内容 | |
618 | + * | |
619 | + * @param key 键 | |
620 | + * @param start 开始 | |
621 | + * @param end 结束 0 到 -1代表所有值 | |
622 | + * @return | |
623 | + */ | |
624 | + public List<Object> lGet(String key, long start, long end) { | |
625 | + try { | |
626 | + return redisTemplate.opsForList().range(key, start, end); | |
627 | + } catch (Exception e) { | |
628 | + e.printStackTrace(); | |
629 | + return null; | |
630 | + } | |
631 | + } | |
632 | + | |
633 | + /** | |
634 | + * 获取list缓存的长度 | |
635 | + * | |
636 | + * @param key 键 | |
637 | + * @return | |
638 | + */ | |
639 | + public long lGetListSize(String key) { | |
640 | + try { | |
641 | + return redisTemplate.opsForList().size(key); | |
642 | + } catch (Exception e) { | |
643 | + e.printStackTrace(); | |
644 | + return 0; | |
645 | + } | |
646 | + } | |
647 | + | |
648 | + /** | |
649 | + * 通过索引 获取list中的值 | |
650 | + * | |
651 | + * @param key 键 | |
652 | + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 | |
653 | + * @return | |
654 | + */ | |
655 | + public Object lGetIndex(String key, long index) { | |
656 | + try { | |
657 | + return redisTemplate.opsForList().index(key, index); | |
658 | + } catch (Exception e) { | |
659 | + e.printStackTrace(); | |
660 | + return null; | |
661 | + } | |
662 | + } | |
663 | + | |
664 | + /** | |
665 | + * 将list放入缓存 | |
666 | + * | |
667 | + * @param key 键 | |
668 | + * @param value 值 | |
669 | + * @return | |
670 | + */ | |
671 | + public boolean lSet(String key, Object value) { | |
672 | + try { | |
673 | + redisTemplate.opsForList().rightPush(key, value); | |
674 | + return true; | |
675 | + } catch (Exception e) { | |
676 | + e.printStackTrace(); | |
677 | + return false; | |
678 | + } | |
679 | + } | |
680 | + | |
681 | + /** | |
682 | + * 将list放入缓存 | |
683 | + * | |
684 | + * @param isShiroRedis 是否shiroredis缓存 | |
685 | + * @param key 键 | |
686 | + * @param value 值 | |
687 | + * @param time 时间(秒) | |
688 | + * @return | |
689 | + */ | |
690 | + public boolean lSet(Boolean isShiroRedis, String key, Object value, long time) { | |
691 | + try { | |
692 | + if (isShiroRedis) { | |
693 | + shiroRedisTemplate.opsForList().rightPush(key, value); | |
694 | + if (time > 0) { | |
695 | + expire(isShiroRedis, key, time); | |
696 | + } | |
697 | + | |
698 | + } else { | |
699 | + redisTemplate.opsForList().rightPush(key, value); | |
700 | + if (time > 0) { | |
701 | + expire(isShiroRedis, key, time); | |
702 | + } | |
703 | + } | |
704 | + | |
705 | + return true; | |
706 | + } catch (Exception e) { | |
707 | + e.printStackTrace(); | |
708 | + return false; | |
709 | + } | |
710 | + } | |
711 | + | |
712 | + /** | |
713 | + * 将list放入缓存 | |
714 | + * | |
715 | + * @param key 键 | |
716 | + * @param value 值 | |
717 | + * @return | |
718 | + */ | |
719 | + public boolean lSet(String key, List<Object> value) { | |
720 | + try { | |
721 | + redisTemplate.opsForList().rightPushAll(key, value); | |
722 | + return true; | |
723 | + } catch (Exception e) { | |
724 | + e.printStackTrace(); | |
725 | + return false; | |
726 | + } | |
727 | + } | |
728 | + | |
729 | + /** | |
730 | + * 将list放入缓存 | |
731 | + * | |
732 | + * @param isShiroRedis shiroredisCache | |
733 | + * @param key 键 | |
734 | + * @param value 值 | |
735 | + * @param time 时间(秒) | |
736 | + * @return | |
737 | + */ | |
738 | + public boolean lSet(Boolean isShiroRedis, String key, List<Object> value, long time) { | |
739 | + try { | |
740 | + | |
741 | + if (isShiroRedis) { | |
742 | + shiroRedisTemplate.opsForList().rightPushAll(key, value); | |
743 | + if (time > 0) { | |
744 | + expire(isShiroRedis, key, time); | |
745 | + } | |
746 | + } else { | |
747 | + redisTemplate.opsForList().rightPushAll(key, value); | |
748 | + if (time > 0) { | |
749 | + expire(isShiroRedis, key, time); | |
750 | + } | |
751 | + } | |
752 | + | |
753 | + return true; | |
754 | + } catch (Exception e) { | |
755 | + e.printStackTrace(); | |
756 | + return false; | |
757 | + } | |
758 | + } | |
759 | + | |
760 | + /** | |
761 | + * 根据索引修改list中的某条数据 | |
762 | + * | |
763 | + * @param key 键 | |
764 | + * @param index 索引 | |
765 | + * @param value 值 | |
766 | + * @return | |
767 | + */ | |
768 | + public boolean lUpdateIndex(String key, long index, Object value) { | |
769 | + try { | |
770 | + redisTemplate.opsForList().set(key, index, value); | |
771 | + return true; | |
772 | + } catch (Exception e) { | |
773 | + e.printStackTrace(); | |
774 | + return false; | |
775 | + } | |
776 | + } | |
777 | + | |
778 | + /** | |
779 | + * 移除N个值为value | |
780 | + * | |
781 | + * @param key 键 | |
782 | + * @param count 移除多少个 | |
783 | + * @param value 值 | |
784 | + * @return 移除的个数 | |
785 | + */ | |
786 | + public long lRemove(String key, long count, Object value) { | |
787 | + try { | |
788 | + Long remove = redisTemplate.opsForList().remove(key, count, value); | |
789 | + return remove; | |
790 | + } catch (Exception e) { | |
791 | + e.printStackTrace(); | |
792 | + return 0; | |
793 | + } | |
794 | + } | |
795 | +} | |
... | ... |
src/main/java/com/huaheng/framework/config/CacheManagerConfig.java
0 → 100644
1 | +package com.huaheng.framework.config; | |
2 | + | |
3 | +import com.huaheng.common.utils.RedisUtils; | |
4 | +import com.huaheng.common.utils.StringUtils; | |
5 | +import com.huaheng.framework.redis.RedisCacheManager; | |
6 | +import org.apache.commons.io.IOUtils; | |
7 | +import org.apache.shiro.cache.ehcache.EhCacheManager; | |
8 | +import org.apache.shiro.config.ConfigurationException; | |
9 | +import org.apache.shiro.io.ResourceUtils; | |
10 | +import org.springframework.beans.factory.annotation.Autowired; | |
11 | +import org.springframework.context.annotation.Bean; | |
12 | +import org.springframework.context.annotation.Configuration; | |
13 | + | |
14 | +import java.io.ByteArrayInputStream; | |
15 | +import java.io.IOException; | |
16 | +import java.io.InputStream; | |
17 | + | |
18 | +@Configuration | |
19 | +public class CacheManagerConfig | |
20 | +{ | |
21 | + @Autowired | |
22 | + private RedisUtils redisUtils; | |
23 | + | |
24 | + /** | |
25 | + * 缓存管理器 使用Ehcache实现 | |
26 | + */ | |
27 | + @Bean | |
28 | + public EhCacheManager getEhCacheManager() | |
29 | + { | |
30 | + | |
31 | + net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("huaheng"); | |
32 | + EhCacheManager em = new EhCacheManager(); | |
33 | + if (StringUtils.isNull(cacheManager)) | |
34 | + { | |
35 | + em.setCacheManager(new net.sf.ehcache.CacheManager( | |
36 | + getCacheManagerConfigFileInputStream("classpath:ehcache/ehcache-shiro.xml"))); | |
37 | + | |
38 | + return em; | |
39 | + } | |
40 | + else | |
41 | + { | |
42 | + em.setCacheManager(cacheManager); | |
43 | + return em; | |
44 | + } | |
45 | + } | |
46 | + | |
47 | + /** | |
48 | + * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署 | |
49 | + */ | |
50 | + protected InputStream getCacheManagerConfigFileInputStream(String configFile) | |
51 | + { | |
52 | + | |
53 | + InputStream inputStream = null; | |
54 | + try | |
55 | + { | |
56 | + inputStream = ResourceUtils.getInputStreamForPath(configFile); | |
57 | + byte[] b = IOUtils.toByteArray(inputStream); | |
58 | + InputStream in = new ByteArrayInputStream(b); | |
59 | + return in; | |
60 | + } | |
61 | + catch (IOException e) | |
62 | + { | |
63 | + throw new ConfigurationException( | |
64 | + "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e); | |
65 | + } | |
66 | + finally | |
67 | + { | |
68 | + IOUtils.closeQuietly(inputStream); | |
69 | + } | |
70 | + } | |
71 | + | |
72 | + /** | |
73 | + * shiro缓存管理器; 需要添加到securityManager中 | |
74 | + * | |
75 | + * @return | |
76 | + */ | |
77 | + @Bean | |
78 | + public RedisCacheManager getRedisCacheManager() | |
79 | + { | |
80 | + RedisCacheManager redisCacheManager = new RedisCacheManager(); | |
81 | + redisCacheManager.setRedisUtils(redisUtils); | |
82 | + // redis中针对不同用户缓存 | |
83 | + redisCacheManager.setPrincipalIdFieldName("loginName"); | |
84 | + // 用户权限信息缓存时间 | |
85 | + redisCacheManager.setExpire(30 * 60 * 1000); | |
86 | + | |
87 | + return redisCacheManager; | |
88 | + } | |
89 | +} | |
... | ... |
src/main/java/com/huaheng/framework/config/RedisConfig.java
0 → 100644
1 | +package com.huaheng.framework.config; | |
2 | + | |
3 | +import com.fasterxml.jackson.annotation.JsonAutoDetect; | |
4 | +import com.fasterxml.jackson.annotation.PropertyAccessor; | |
5 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
6 | +import com.huaheng.framework.redis.serializer.SerializeUtils; | |
7 | +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; | |
8 | +import org.springframework.beans.factory.annotation.Value; | |
9 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
10 | +import org.springframework.cache.CacheManager; | |
11 | +import org.springframework.cache.annotation.CachingConfigurerSupport; | |
12 | +import org.springframework.cache.annotation.EnableCaching; | |
13 | +import org.springframework.cache.interceptor.KeyGenerator; | |
14 | +import org.springframework.context.annotation.Bean; | |
15 | +import org.springframework.context.annotation.Configuration; | |
16 | +import org.springframework.context.annotation.Scope; | |
17 | +import org.springframework.data.redis.cache.RedisCacheConfiguration; | |
18 | +import org.springframework.data.redis.cache.RedisCacheManager; | |
19 | +import org.springframework.data.redis.connection.RedisPassword; | |
20 | +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; | |
21 | +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; | |
22 | +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; | |
23 | +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; | |
24 | +import org.springframework.data.redis.core.RedisTemplate; | |
25 | +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | |
26 | +import org.springframework.data.redis.serializer.RedisSerializationContext; | |
27 | +import org.springframework.data.redis.serializer.RedisSerializer; | |
28 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | |
29 | + | |
30 | +import java.lang.reflect.Method; | |
31 | +import java.time.Duration; | |
32 | + | |
33 | +@Configuration | |
34 | +@EnableCaching // 开启缓存支持 | |
35 | +public class RedisConfig extends CachingConfigurerSupport | |
36 | +{ | |
37 | + @Value("${redis.database}") | |
38 | + private int database; | |
39 | + | |
40 | + @Value("${redis.host}") | |
41 | + private String host; | |
42 | + | |
43 | + @Value("${redis.port}") | |
44 | + private int port; | |
45 | + | |
46 | + @Value("${redis.password}") | |
47 | + private String password; | |
48 | + | |
49 | + @Value("${redis.ssl}") | |
50 | + private Boolean ssl; | |
51 | + | |
52 | + @Value("${redis.lettuce.pool.max-idle}") | |
53 | + private int maxIdle; | |
54 | + | |
55 | + @Value("${redis.lettuce.pool.min-idle}") | |
56 | + private int minIdle; | |
57 | + | |
58 | + @Value("${redis.lettuce.pool.max-total}") | |
59 | + private int maxTotal; | |
60 | + | |
61 | + @Value("${redis.lettuce.pool.max-waitMillis}") | |
62 | + private long maxWaitMillis; | |
63 | + | |
64 | + @Value("${redis.timeout}") | |
65 | + private long timeout; | |
66 | + | |
67 | + private Duration timeToLive = Duration.ofSeconds(600); | |
68 | + | |
69 | + /** | |
70 | + * 在没有指定缓存Key的情况下,key生成策略 | |
71 | + */ | |
72 | + @Bean | |
73 | + public KeyGenerator keyGenerator() | |
74 | + { | |
75 | + return new KeyGenerator() | |
76 | + { | |
77 | + @Override | |
78 | + public Object generate(Object target, Method method, Object... params) | |
79 | + { | |
80 | + StringBuffer sb = new StringBuffer(); | |
81 | + sb.append(target.getClass().getName()); | |
82 | + sb.append(method.getName()); | |
83 | + for (Object obj : params) | |
84 | + { | |
85 | + sb.append(obj.toString()); | |
86 | + } | |
87 | + return sb.toString(); | |
88 | + } | |
89 | + }; | |
90 | + } | |
91 | + | |
92 | + // 缓存管理器 使用Lettuce,和jedis有很大不同LettuceConnectionFactory lettuceConnectionFactory | |
93 | + @Bean | |
94 | + public CacheManager cacheManager() | |
95 | + { | |
96 | + // 关键点,spring cache的注解使用的序列化都从这来,没有这个配置的话使用的jdk自己的序列化,实际上不影响使用,只是打印出来不适合人眼识别 | |
97 | + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() | |
98 | + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))// key序列化方式 | |
99 | + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(getValueSerializer()))// value序列化方式 | |
100 | + .disableCachingNullValues().entryTtl(timeToLive).disableCachingNullValues(); | |
101 | + ;// 缓存过期时间 | |
102 | + | |
103 | + RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder | |
104 | + .fromConnectionFactory(lettuceConnectionFactory())// 默认有锁 等待锁时间为0 | |
105 | + .cacheDefaults(redisCacheConfiguration).transactionAware(); | |
106 | + return builder.build(); | |
107 | + } | |
108 | + | |
109 | + /** | |
110 | + * RedisTemplate配置 使用自定义redisTemplate的时候 重新定义序列化方式 LettuceConnectionFactory lettuceConnectionFactory | |
111 | + */ | |
112 | + @Bean | |
113 | + public RedisTemplate<String, Object> redisTemplate() | |
114 | + { | |
115 | + // 配置redisTemplate | |
116 | + RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); | |
117 | + redisTemplate.setConnectionFactory(lettuceConnectionFactory()); | |
118 | + | |
119 | + RedisSerializer<?> stringSerializer = new StringRedisSerializer(); | |
120 | + | |
121 | + redisTemplate.setKeySerializer(stringSerializer);// key序列化 | |
122 | + redisTemplate.setValueSerializer(getValueSerializer());// value序列化new LZ4Serializer(getValueSerializer()) | |
123 | + redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化 | |
124 | + redisTemplate.setHashValueSerializer(getValueSerializer());// Hash value序列化 | |
125 | + redisTemplate.afterPropertiesSet(); | |
126 | + | |
127 | + return redisTemplate; | |
128 | + } | |
129 | + | |
130 | + /** | |
131 | + * shiroRedisTemplate配置 使用自定义shiroRedisTemplate的时候 重新定义序列化方式 LettuceConnectionFactory lettuceConnectionFactory | |
132 | + */ | |
133 | + @Bean | |
134 | + public RedisTemplate<String, Object> shiroRedisTemplate() | |
135 | + { | |
136 | + // 配置redisTemplate | |
137 | + RedisTemplate<String, Object> shiroRedisTemplate = new RedisTemplate<String, Object>(); | |
138 | + shiroRedisTemplate.setConnectionFactory(lettuceConnectionFactory()); | |
139 | + | |
140 | + RedisSerializer<?> stringSerializer = new StringRedisSerializer(); | |
141 | + | |
142 | + shiroRedisTemplate.setKeySerializer(stringSerializer);// key序列化 | |
143 | + shiroRedisTemplate.setValueSerializer(new SerializeUtils<Object>());// value序列化 | |
144 | + shiroRedisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化 | |
145 | + shiroRedisTemplate.setHashValueSerializer(new SerializeUtils<Object>());// Hash value序列化 | |
146 | + shiroRedisTemplate.afterPropertiesSet(); | |
147 | + | |
148 | + return shiroRedisTemplate; | |
149 | + } | |
150 | + | |
151 | + private RedisSerializer<String> keySerializer() | |
152 | + { | |
153 | + return new StringRedisSerializer(); | |
154 | + } | |
155 | + | |
156 | + private RedisSerializer<Object> getValueSerializer() | |
157 | + { | |
158 | + // 设置序列化 | |
159 | + Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( | |
160 | + Object.class); | |
161 | + ObjectMapper om = new ObjectMapper(); | |
162 | + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | |
163 | + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); | |
164 | + jackson2JsonRedisSerializer.setObjectMapper(om); | |
165 | + return jackson2JsonRedisSerializer; | |
166 | + } | |
167 | + | |
168 | + // 单机版配置连接参数 | |
169 | + @Bean | |
170 | + public RedisStandaloneConfiguration redisStandaloneConfiguration() | |
171 | + { | |
172 | + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); | |
173 | + redisStandaloneConfiguration.setDatabase(database); | |
174 | + redisStandaloneConfiguration.setHostName(host); | |
175 | + redisStandaloneConfiguration.setPort(port); | |
176 | + redisStandaloneConfiguration.setPassword(RedisPassword.of(password)); | |
177 | + | |
178 | + // 集群版配置 | |
179 | + // RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); | |
180 | + // String[] serverArray = clusterNodes.split(","); | |
181 | + // Set<RedisNode> nodes = new HashSet<RedisNode>(); | |
182 | + // for (String ipPort : serverArray) { | |
183 | + // String[] ipAndPort = ipPort.split(":"); | |
184 | + // nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); | |
185 | + // } | |
186 | + // redisClusterConfiguration.setPassword(RedisPassword.of(password)); | |
187 | + // redisClusterConfiguration.setClusterNodes(nodes); | |
188 | + // redisClusterConfiguration.setMaxRedirects(maxRedirects); | |
189 | + | |
190 | + return redisStandaloneConfiguration; | |
191 | + } | |
192 | + | |
193 | + /** | |
194 | + * 配置LettuceClientConfiguration 包括线程池配置和安全项配置 genericObjectPoolConfig common-pool2线程池GenericObjectPoolConfig | |
195 | + * genericObjectPoolConfig | |
196 | + * | |
197 | + * @return lettuceClientConfiguration | |
198 | + */ | |
199 | + @Bean | |
200 | + public LettuceClientConfiguration lettuceClientConfiguration() | |
201 | + { | |
202 | + LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder() | |
203 | + .commandTimeout(Duration.ofMillis(timeout)).shutdownTimeout(Duration.ofMillis(200)) | |
204 | + .poolConfig(genericObjectPoolConfig()).build(); | |
205 | + if (ssl) | |
206 | + { | |
207 | + lettuceClientConfiguration.isUseSsl(); | |
208 | + } | |
209 | + return lettuceClientConfiguration; | |
210 | + } | |
211 | + | |
212 | + // 设置连接工厂 | |
213 | + @Bean | |
214 | + public LettuceConnectionFactory lettuceConnectionFactory() | |
215 | + { | |
216 | + return new LettuceConnectionFactory(redisStandaloneConfiguration(), lettuceClientConfiguration()); | |
217 | + } | |
218 | + | |
219 | + /** | |
220 | + * GenericObjectPoolConfig 连接池配置 | |
221 | + */ | |
222 | + @Bean | |
223 | + @ConfigurationProperties(prefix = "redis.lettuce.pool") | |
224 | + @Scope(value = "prototype") | |
225 | + @SuppressWarnings("rawtypes") | |
226 | + public GenericObjectPoolConfig genericObjectPoolConfig() | |
227 | + { | |
228 | + return new GenericObjectPoolConfig(); | |
229 | + } | |
230 | +} | |
... | ... |
src/main/java/com/huaheng/framework/config/ShiroConfig.java
... | ... | @@ -12,6 +12,7 @@ import org.apache.shiro.spring.web.ShiroFilterFactoryBean; |
12 | 12 | import org.apache.shiro.web.mgt.CookieRememberMeManager; |
13 | 13 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; |
14 | 14 | import org.apache.shiro.web.servlet.SimpleCookie; |
15 | +import org.springframework.beans.factory.annotation.Autowired; | |
15 | 16 | import org.springframework.beans.factory.annotation.Qualifier; |
16 | 17 | import org.springframework.beans.factory.annotation.Value; |
17 | 18 | import org.springframework.context.annotation.Bean; |
... | ... | @@ -36,11 +37,15 @@ import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; |
36 | 37 | public class ShiroConfig { |
37 | 38 | public static final String PREMISSION_STRING = "perms[\"{0}\"]"; |
38 | 39 | |
39 | - // Session超时时间,单位为毫秒(默认30分钟) | |
40 | + @Value("${shiro.session.redisEnabled}") | |
41 | + private boolean redisEnabled; | |
42 | + | |
43 | + | |
44 | + // Session超时时间,单位为毫秒 | |
40 | 45 | @Value("${shiro.session.expireTime}") |
41 | 46 | private int expireTime; |
42 | 47 | |
43 | - // 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟 | |
48 | + // 相隔多久检查一次session的有效性,单位毫秒 | |
44 | 49 | @Value("${shiro.session.validationInterval}") |
45 | 50 | private int validationInterval; |
46 | 51 | |
... | ... | @@ -80,29 +85,33 @@ public class ShiroConfig { |
80 | 85 | @Value("${shiro.user.unauthorizedUrl}") |
81 | 86 | private String unauthorizedUrl; |
82 | 87 | |
88 | + @Autowired | |
89 | + private CacheManagerConfig cacheManagerConfig; | |
90 | + | |
83 | 91 | /** |
84 | 92 | * 缓存管理器 使用Ehcache实现 |
85 | 93 | */ |
86 | - @Bean | |
87 | - public EhCacheManager getEhCacheManager() { | |
88 | - net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("huaheng"); | |
89 | - EhCacheManager em = new EhCacheManager(); | |
90 | - if (StringUtils.isNull(cacheManager)) { | |
91 | - em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml"); | |
92 | - return em; | |
93 | - } else { | |
94 | - em.setCacheManager(cacheManager); | |
95 | - return em; | |
96 | - } | |
97 | - } | |
94 | + //@Bean | |
95 | + //public EhCacheManager getEhCacheManager() { | |
96 | + // net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("huaheng"); | |
97 | + // EhCacheManager em = new EhCacheManager(); | |
98 | + // if (StringUtils.isNull(cacheManager)) { | |
99 | + // em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml"); | |
100 | + // return em; | |
101 | + // } else { | |
102 | + // em.setCacheManager(cacheManager); | |
103 | + // return em; | |
104 | + // } | |
105 | + //} | |
98 | 106 | |
99 | 107 | /** |
100 | 108 | * 自定义Realm |
101 | 109 | */ |
102 | 110 | @Bean |
103 | - public UserRealm userRealm(EhCacheManager cacheManager) { | |
111 | + public UserRealm userRealm() { | |
104 | 112 | UserRealm userRealm = new UserRealm(); |
105 | - userRealm.setCacheManager(cacheManager); | |
113 | + userRealm.setCacheManager( | |
114 | + redisEnabled ? cacheManagerConfig.getRedisCacheManager() : cacheManagerConfig.getEhCacheManager()); | |
106 | 115 | return userRealm; |
107 | 116 | } |
108 | 117 | |
... | ... | @@ -131,7 +140,7 @@ public class ShiroConfig { |
131 | 140 | public SpringSessionValidationScheduler sessionValidationScheduler() { |
132 | 141 | SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler(); |
133 | 142 | // 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟 |
134 | - sessionValidationScheduler.setSessionValidationInterval(validationInterval * 60 * 1000); | |
143 | + sessionValidationScheduler.setSessionValidationInterval(validationInterval); | |
135 | 144 | // 设置会话验证调度器进行会话验证时的会话管理器 |
136 | 145 | sessionValidationScheduler.setSessionManager(sessionValidationManager()); |
137 | 146 | return sessionValidationScheduler; |
... | ... | @@ -144,11 +153,11 @@ public class ShiroConfig { |
144 | 153 | public OnlineWebSessionManager sessionValidationManager() { |
145 | 154 | OnlineWebSessionManager manager = new OnlineWebSessionManager(); |
146 | 155 | // 加入缓存管理器 |
147 | - manager.setCacheManager(getEhCacheManager()); | |
156 | + manager.setCacheManager(redisEnabled ? cacheManagerConfig.getRedisCacheManager() : cacheManagerConfig.getEhCacheManager()); | |
148 | 157 | // 删除过期的session |
149 | 158 | manager.setDeleteInvalidSessions(true); |
150 | 159 | // 设置全局session超时时间 |
151 | - manager.setGlobalSessionTimeout(expireTime * 60 * 1000); | |
160 | + manager.setGlobalSessionTimeout(expireTime); | |
152 | 161 | // 去掉 JSESSIONID |
153 | 162 | manager.setSessionIdUrlRewritingEnabled(false); |
154 | 163 | // 是否定时检查session |
... | ... | @@ -167,11 +176,11 @@ public class ShiroConfig { |
167 | 176 | public OnlineWebSessionManager sessionManager() { |
168 | 177 | OnlineWebSessionManager manager = new OnlineWebSessionManager(); |
169 | 178 | // 加入缓存管理器 |
170 | - manager.setCacheManager(getEhCacheManager()); | |
179 | + manager.setCacheManager(redisEnabled ? cacheManagerConfig.getRedisCacheManager() : cacheManagerConfig.getEhCacheManager()); | |
171 | 180 | // 删除过期的session |
172 | 181 | manager.setDeleteInvalidSessions(true); |
173 | 182 | // 设置全局session超时时间 |
174 | - manager.setGlobalSessionTimeout(expireTime * 60 * 1000); | |
183 | + manager.setGlobalSessionTimeout(expireTime); | |
175 | 184 | // 去掉 JSESSIONID |
176 | 185 | manager.setSessionIdUrlRewritingEnabled(false); |
177 | 186 | // 定义要使用的无效的Session定时调度器 |
... | ... | @@ -196,7 +205,9 @@ public class ShiroConfig { |
196 | 205 | // 记住我 |
197 | 206 | securityManager.setRememberMeManager(rememberMeManager()); |
198 | 207 | // 注入缓存管理器; |
199 | - securityManager.setCacheManager(getEhCacheManager()); | |
208 | + securityManager.setCacheManager( | |
209 | + redisEnabled ? cacheManagerConfig.getRedisCacheManager() : cacheManagerConfig.getEhCacheManager()); | |
210 | + | |
200 | 211 | // session管理器 |
201 | 212 | securityManager.setSessionManager(sessionManager()); |
202 | 213 | return securityManager; |
... | ... | @@ -288,7 +299,7 @@ public class ShiroConfig { |
288 | 299 | //filterChainDefinitionMap.put("/mobile/inventory/completeTaskListByWMS", "anon"); |
289 | 300 | //filterChainDefinitionMap.put("/receipt/receiving/saveBatch", "anon"); |
290 | 301 | //filterChainDefinitionMap.put("/config/zone/getAllFlatLocation", "anon"); |
291 | - filterChainDefinitionMap.put("/mobile/getModules2", "anon"); | |
302 | + //filterChainDefinitionMap.put("/mobile/getModules2", "anon"); | |
292 | 303 | |
293 | 304 | |
294 | 305 | // 系统权限列表 |
... | ... | @@ -337,7 +348,7 @@ public class ShiroConfig { |
337 | 348 | cookie.setDomain(domain); |
338 | 349 | cookie.setPath(path); |
339 | 350 | cookie.setHttpOnly(httpOnly); |
340 | - cookie.setMaxAge(maxAge * 24 * 60 * 1000); | |
351 | + cookie.setMaxAge(maxAge * 24 * 60 * 60); | |
341 | 352 | return cookie; |
342 | 353 | } |
343 | 354 | |
... | ... |
src/main/java/com/huaheng/framework/redis/Exception/CacheManagerPrincipalIdNotAssignedException.java
0 → 100644
1 | +package com.huaheng.framework.redis.Exception; | |
2 | + | |
3 | +@SuppressWarnings("serial") | |
4 | +public class CacheManagerPrincipalIdNotAssignedException extends RuntimeException | |
5 | +{ | |
6 | + private static final String MESSAGE = "CacheManager didn't assign Principal Id field name!"; | |
7 | + | |
8 | + public CacheManagerPrincipalIdNotAssignedException() | |
9 | + { | |
10 | + super(MESSAGE); | |
11 | + } | |
12 | +} | |
... | ... |
src/main/java/com/huaheng/framework/redis/Exception/PrincipalIdNullException.java
0 → 100644
1 | +package com.huaheng.framework.redis.Exception; | |
2 | + | |
3 | +@SuppressWarnings("serial") | |
4 | +public class PrincipalIdNullException extends RuntimeException | |
5 | +{ | |
6 | + private static final String MESSAGE = "Principal Id shouldn't be null!"; | |
7 | + | |
8 | + @SuppressWarnings("rawtypes") | |
9 | + public PrincipalIdNullException(Class clazz, String idMethodName) | |
10 | + { | |
11 | + super(clazz + " id field: " + idMethodName + ", value is null\n" + MESSAGE); | |
12 | + } | |
13 | +} | |
... | ... |
src/main/java/com/huaheng/framework/redis/Exception/PrincipalInstanceException.java
0 → 100644
1 | +package com.huaheng.framework.redis.Exception; | |
2 | + | |
3 | +@SuppressWarnings("serial") | |
4 | +public class PrincipalInstanceException extends RuntimeException | |
5 | +{ | |
6 | + private static final String MESSAGE = "We need a field to identify this Cache Object in Redis. " | |
7 | + + "So you need to defined an id field which you can get unique id to identify this principal. " | |
8 | + + "For example, if you use UserInfo as Principal class, the id field maybe userId, userName, email, etc. " | |
9 | + + "For example, getUserId(), getUserName(), getEmail(), etc.\n" | |
10 | + + "Default value is authCacheKey or id, that means your principal object has a method called \"getAuthCacheKey()\" or \"getId()\""; | |
11 | + | |
12 | + @SuppressWarnings("rawtypes") | |
13 | + public PrincipalInstanceException(Class clazz, String idMethodName) | |
14 | + { | |
15 | + super(clazz + " must has getter for field: " + idMethodName + "\n" + MESSAGE); | |
16 | + } | |
17 | + | |
18 | + @SuppressWarnings("rawtypes") | |
19 | + public PrincipalInstanceException(Class clazz, String idMethodName, Exception e) | |
20 | + { | |
21 | + super(clazz + " must has getter for field: " + idMethodName + "\n" + MESSAGE, e); | |
22 | + } | |
23 | +} | |
... | ... |
src/main/java/com/huaheng/framework/redis/RedisCacheManager.java
0 → 100644
1 | +package com.huaheng.framework.redis; | |
2 | + | |
3 | +import com.huaheng.common.utils.RedisUtils; | |
4 | +import org.apache.shiro.cache.Cache; | |
5 | +import org.apache.shiro.cache.CacheException; | |
6 | +import org.apache.shiro.cache.CacheManager; | |
7 | +import org.slf4j.Logger; | |
8 | +import org.slf4j.LoggerFactory; | |
9 | +import org.springframework.beans.factory.annotation.Autowired; | |
10 | +import org.springframework.beans.factory.annotation.Value; | |
11 | +import org.springframework.stereotype.Component; | |
12 | + | |
13 | +import java.util.concurrent.ConcurrentHashMap; | |
14 | +import java.util.concurrent.ConcurrentMap; | |
15 | + | |
16 | +@Component | |
17 | +public class RedisCacheManager implements CacheManager | |
18 | +{ | |
19 | + private final Logger logger = LoggerFactory.getLogger(RedisCacheManager.class); | |
20 | + | |
21 | + @SuppressWarnings({ "unchecked", "rawtypes" }) | |
22 | + private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap(); | |
23 | + | |
24 | + @Autowired | |
25 | + private RedisUtils redisUtils; | |
26 | + | |
27 | + public static final String DEFAULT_CACHE_KEY_PREFIX = "shiro:redisCache:"; | |
28 | + private String keyPrefix = DEFAULT_CACHE_KEY_PREFIX; | |
29 | + | |
30 | + // expire time in seconds | |
31 | + public static final long DEFAULT_EXPIRE = 30 * 60 * 1000; | |
32 | + @Value("${redis.timeout}") | |
33 | + private long expire ; | |
34 | + | |
35 | + public static final String DEFAULT_PRINCIPAL_ID_FIELD_NAME = "authCacheKey or id"; | |
36 | + private String principalIdFieldName = DEFAULT_PRINCIPAL_ID_FIELD_NAME; | |
37 | + | |
38 | + @SuppressWarnings({ "rawtypes", "unchecked" }) | |
39 | + @Override | |
40 | + public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException | |
41 | + { | |
42 | + this.logger.debug("get cache, cacheName=" + cacheName); | |
43 | + Cache cache = (Cache) this.caches.get(cacheName); | |
44 | + if (cache == null) | |
45 | + { | |
46 | + this.logger.debug("get cache but cache is null"); | |
47 | + cache = new ShiroRedisCache(this.redisUtils, this.keyPrefix + cacheName + ":", expire, | |
48 | + this.principalIdFieldName); | |
49 | + this.caches.put(cacheName, cache); | |
50 | + } | |
51 | + | |
52 | + return (Cache) cache; | |
53 | + } | |
54 | + | |
55 | + public String getKeyPrefix() | |
56 | + { | |
57 | + return keyPrefix; | |
58 | + } | |
59 | + | |
60 | + public void setKeyPrefix(String keyPrefix) | |
61 | + { | |
62 | + this.keyPrefix = keyPrefix; | |
63 | + } | |
64 | + | |
65 | + public long getExpire() | |
66 | + { | |
67 | + return expire; | |
68 | + } | |
69 | + | |
70 | + public void setExpire(long expire) | |
71 | + { | |
72 | + this.expire = expire; | |
73 | + } | |
74 | + | |
75 | + public String getPrincipalIdFieldName() | |
76 | + { | |
77 | + return principalIdFieldName; | |
78 | + } | |
79 | + | |
80 | + public void setPrincipalIdFieldName(String principalIdFieldName) | |
81 | + { | |
82 | + this.principalIdFieldName = principalIdFieldName; | |
83 | + } | |
84 | + | |
85 | + public void setRedisUtils(RedisUtils redisUtils) | |
86 | + { | |
87 | + this.redisUtils = redisUtils; | |
88 | + } | |
89 | +} | |
... | ... |
src/main/java/com/huaheng/framework/redis/ShiroRedisCache.java
0 → 100644
1 | +package com.huaheng.framework.redis; | |
2 | + | |
3 | +import com.huaheng.common.utils.RedisUtils; | |
4 | +import com.huaheng.framework.redis.Exception.CacheManagerPrincipalIdNotAssignedException; | |
5 | +import com.huaheng.framework.redis.Exception.PrincipalIdNullException; | |
6 | +import com.huaheng.framework.redis.Exception.PrincipalInstanceException; | |
7 | +import org.apache.shiro.cache.Cache; | |
8 | +import org.apache.shiro.cache.CacheException; | |
9 | +import org.apache.shiro.subject.PrincipalCollection; | |
10 | +import org.apache.shiro.util.CollectionUtils; | |
11 | +import org.slf4j.Logger; | |
12 | +import org.slf4j.LoggerFactory; | |
13 | +import org.springframework.data.redis.serializer.SerializationException; | |
14 | + | |
15 | +import java.lang.reflect.InvocationTargetException; | |
16 | +import java.lang.reflect.Method; | |
17 | +import java.util.*; | |
18 | + | |
19 | +public class ShiroRedisCache<K, V> implements Cache<K, V> | |
20 | +{ | |
21 | + private final Logger logger = LoggerFactory.getLogger(ShiroRedisCache.class); | |
22 | + | |
23 | + private RedisUtils redisUtils; | |
24 | + | |
25 | + private String keyPrefix = RedisCacheManager.DEFAULT_CACHE_KEY_PREFIX; | |
26 | + | |
27 | + private long expire = RedisCacheManager.DEFAULT_EXPIRE; | |
28 | + | |
29 | + private String principalIdFieldName = RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME; | |
30 | + | |
31 | + public ShiroRedisCache(RedisUtils redisUtils, String prefix, long expire, String principalIdFieldName) | |
32 | + { | |
33 | + if (redisUtils == null) | |
34 | + { | |
35 | + throw new IllegalArgumentException("redisUtils cannot be null."); | |
36 | + } | |
37 | + this.redisUtils = redisUtils; | |
38 | + if (prefix != null && !"".equals(prefix)) | |
39 | + { | |
40 | + this.keyPrefix = prefix; | |
41 | + } | |
42 | + if (expire != -1) | |
43 | + { | |
44 | + this.expire = expire; | |
45 | + } | |
46 | + if (principalIdFieldName != null && !"".equals(principalIdFieldName)) | |
47 | + { | |
48 | + this.principalIdFieldName = principalIdFieldName; | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + @SuppressWarnings("unchecked") | |
53 | + @Override | |
54 | + public V get(K key) throws CacheException | |
55 | + { | |
56 | + if (key == null) | |
57 | + { | |
58 | + return null; | |
59 | + } | |
60 | + else | |
61 | + { | |
62 | + | |
63 | + String redisCacheKey = getRedisCacheKey(key); | |
64 | + Object rawValue = redisUtils.get(true, redisCacheKey); | |
65 | + | |
66 | + if (rawValue == null) | |
67 | + { | |
68 | + return null; | |
69 | + } | |
70 | + V value = (V) rawValue; | |
71 | + return value; | |
72 | + } | |
73 | + | |
74 | + } | |
75 | + | |
76 | + @Override | |
77 | + public V put(K key, V value) throws CacheException | |
78 | + { | |
79 | + if (key == null) | |
80 | + { | |
81 | + logger.warn("Saving a null key is meaningless, return value directly without call Redis."); | |
82 | + return value; | |
83 | + } | |
84 | + try | |
85 | + { | |
86 | + String redisCacheKey = getRedisCacheKey(key); | |
87 | + redisUtils.set(true, redisCacheKey, value != null ? value : null, expire); | |
88 | + return value; | |
89 | + } | |
90 | + catch (SerializationException e) | |
91 | + { | |
92 | + throw new CacheException(e); | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + private String getRedisCacheKey(K key) | |
97 | + { | |
98 | + if (key == null) | |
99 | + { | |
100 | + return null; | |
101 | + } | |
102 | + | |
103 | + return this.keyPrefix + getStringRedisKey(key); | |
104 | + | |
105 | + } | |
106 | + | |
107 | + private String getStringRedisKey(K key) | |
108 | + { | |
109 | + String redisKey; | |
110 | + if (key instanceof PrincipalCollection) | |
111 | + { | |
112 | + redisKey = getRedisKeyFromPrincipalIdField((PrincipalCollection) key); | |
113 | + } | |
114 | + else | |
115 | + { | |
116 | + redisKey = key.toString(); | |
117 | + } | |
118 | + return redisKey; | |
119 | + } | |
120 | + | |
121 | + /** | |
122 | + * getRedisKeyFromPrincipalIdField()是获取缓存的用户身份信息 和用户权限信息。 里面有一个属性principalIdFieldName | |
123 | + * 在RedisCacheManager也有这个属性,设置其中一个就可以.是为了给缓存用户身份和权限信息在Redis中的key唯一, 登录用户名可能是username 或者 phoneNum 或者是Email中的一个, 如 | |
124 | + * 我的User实体类中 有一个 usernane字段,也是登录时候使用的用户名,在redis中缓存的权限信息key 如下, 这个admin 就是 通过getUsername获得的。 | |
125 | + */ | |
126 | + | |
127 | + private String getRedisKeyFromPrincipalIdField(PrincipalCollection key) | |
128 | + { | |
129 | + Object principalObject = key.getPrimaryPrincipal(); | |
130 | + if (principalObject instanceof String) | |
131 | + { | |
132 | + return principalObject.toString(); | |
133 | + } | |
134 | + Method pincipalIdGetter = getPrincipalIdGetter(principalObject); | |
135 | + return getIdObj(principalObject, pincipalIdGetter); | |
136 | + } | |
137 | + | |
138 | + private String getIdObj(Object principalObject, Method pincipalIdGetter) | |
139 | + { | |
140 | + String redisKey; | |
141 | + try | |
142 | + { | |
143 | + Object idObj = pincipalIdGetter.invoke(principalObject); | |
144 | + if (idObj == null) | |
145 | + { | |
146 | + throw new PrincipalIdNullException(principalObject.getClass(), this.principalIdFieldName); | |
147 | + } | |
148 | + redisKey = idObj.toString(); | |
149 | + } | |
150 | + catch (IllegalAccessException e) | |
151 | + { | |
152 | + throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e); | |
153 | + } | |
154 | + catch (InvocationTargetException e) | |
155 | + { | |
156 | + throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e); | |
157 | + } | |
158 | + return redisKey; | |
159 | + } | |
160 | + | |
161 | + private Method getPrincipalIdGetter(Object principalObject) | |
162 | + { | |
163 | + Method pincipalIdGetter = null; | |
164 | + String principalIdMethodName = this.getPrincipalIdMethodName(); | |
165 | + try | |
166 | + { | |
167 | + pincipalIdGetter = principalObject.getClass().getMethod(principalIdMethodName); | |
168 | + } | |
169 | + catch (NoSuchMethodException e) | |
170 | + { | |
171 | + throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName); | |
172 | + } | |
173 | + return pincipalIdGetter; | |
174 | + } | |
175 | + | |
176 | + private String getPrincipalIdMethodName() | |
177 | + { | |
178 | + if (this.principalIdFieldName == null || "".equals(this.principalIdFieldName)) | |
179 | + { | |
180 | + throw new CacheManagerPrincipalIdNotAssignedException(); | |
181 | + } | |
182 | + return "get" + this.principalIdFieldName.substring(0, 1).toUpperCase() + this.principalIdFieldName.substring(1); | |
183 | + } | |
184 | + | |
185 | + @SuppressWarnings("unchecked") | |
186 | + @Override | |
187 | + public V remove(K key) throws CacheException | |
188 | + { | |
189 | + if (key == null) | |
190 | + { | |
191 | + return null; | |
192 | + } | |
193 | + try | |
194 | + { | |
195 | + String redisCacheKey = getRedisCacheKey(key); | |
196 | + Object rawValue = redisUtils.get(true, redisCacheKey); | |
197 | + V previous = (V) rawValue; | |
198 | + redisUtils.del(true, redisCacheKey); | |
199 | + return previous; | |
200 | + } | |
201 | + catch (SerializationException e) | |
202 | + { | |
203 | + throw new CacheException(e); | |
204 | + } | |
205 | + } | |
206 | + | |
207 | + @Override | |
208 | + public void clear() throws CacheException | |
209 | + { | |
210 | + Set<String> keys = null; | |
211 | + try | |
212 | + { | |
213 | + keys = redisUtils.scan(true, this.keyPrefix + "*"); | |
214 | + } | |
215 | + catch (SerializationException e) | |
216 | + { | |
217 | + logger.error("get keys error", e); | |
218 | + } | |
219 | + if (keys == null || keys.size() == 0) | |
220 | + { | |
221 | + return; | |
222 | + } | |
223 | + for (String key : keys) | |
224 | + { | |
225 | + redisUtils.del(true, key); | |
226 | + } | |
227 | + } | |
228 | + | |
229 | + @Override | |
230 | + public int size() | |
231 | + { | |
232 | + Long longSize = 0L; | |
233 | + try | |
234 | + { | |
235 | + longSize = new Long(redisUtils.scanSize(true, this.keyPrefix + "*")); | |
236 | + } | |
237 | + catch (SerializationException e) | |
238 | + { | |
239 | + logger.error("get keys error", e); | |
240 | + } | |
241 | + return longSize.intValue(); | |
242 | + } | |
243 | + | |
244 | + @SuppressWarnings("unchecked") | |
245 | + @Override | |
246 | + public Set<K> keys() | |
247 | + { | |
248 | + Set<String> keys = null; | |
249 | + try | |
250 | + { | |
251 | + keys = redisUtils.scan(true, this.keyPrefix + "*"); | |
252 | + } | |
253 | + catch (SerializationException e) | |
254 | + { | |
255 | + logger.error("get keys error", e); | |
256 | + return Collections.emptySet(); | |
257 | + } | |
258 | + | |
259 | + if (CollectionUtils.isEmpty(keys)) | |
260 | + { | |
261 | + return Collections.emptySet(); | |
262 | + } | |
263 | + | |
264 | + Set<K> convertedKeys = new HashSet<K>(); | |
265 | + for (String key : keys) | |
266 | + { | |
267 | + try | |
268 | + { | |
269 | + convertedKeys.add((K) key); | |
270 | + } | |
271 | + catch (SerializationException e) | |
272 | + { | |
273 | + logger.error("deserialize keys error", e); | |
274 | + } | |
275 | + } | |
276 | + return convertedKeys; | |
277 | + } | |
278 | + | |
279 | + @SuppressWarnings("unchecked") | |
280 | + @Override | |
281 | + public Collection<V> values() | |
282 | + { | |
283 | + Set<String> keys = null; | |
284 | + try | |
285 | + { | |
286 | + keys = redisUtils.scan(true, this.keyPrefix + "*"); | |
287 | + } | |
288 | + catch (SerializationException e) | |
289 | + { | |
290 | + logger.error("get values error", e); | |
291 | + return Collections.emptySet(); | |
292 | + } | |
293 | + | |
294 | + if (CollectionUtils.isEmpty(keys)) | |
295 | + { | |
296 | + return Collections.emptySet(); | |
297 | + } | |
298 | + | |
299 | + List<V> values = new ArrayList<V>(keys.size()); | |
300 | + for (String key : keys) | |
301 | + { | |
302 | + V value = null; | |
303 | + try | |
304 | + { | |
305 | + value = (V) redisUtils.get(true, key); | |
306 | + } | |
307 | + catch (SerializationException e) | |
308 | + { | |
309 | + logger.error("deserialize values= error", e); | |
310 | + } | |
311 | + if (value != null) | |
312 | + { | |
313 | + values.add(value); | |
314 | + } | |
315 | + } | |
316 | + return Collections.unmodifiableList(values); | |
317 | + } | |
318 | + | |
319 | + public String getKeyPrefix() | |
320 | + { | |
321 | + return keyPrefix; | |
322 | + } | |
323 | + | |
324 | + public void setKeyPrefix(String keyPrefix) | |
325 | + { | |
326 | + this.keyPrefix = keyPrefix; | |
327 | + } | |
328 | + | |
329 | + public long getExpire() | |
330 | + { | |
331 | + return expire; | |
332 | + } | |
333 | + | |
334 | + public void setExpire(long expire) | |
335 | + { | |
336 | + this.expire = expire; | |
337 | + } | |
338 | + | |
339 | + public String getPrincipalIdFieldName() | |
340 | + { | |
341 | + return principalIdFieldName; | |
342 | + } | |
343 | + | |
344 | + public void setPrincipalIdFieldName(String principalIdFieldName) | |
345 | + { | |
346 | + this.principalIdFieldName = principalIdFieldName; | |
347 | + } | |
348 | + | |
349 | + public void setRedisUtils(RedisUtils redisUtils) | |
350 | + { | |
351 | + this.redisUtils = redisUtils; | |
352 | + } | |
353 | +} | |
... | ... |
src/main/java/com/huaheng/framework/redis/serializer/SerializeUtils.java
0 → 100644
1 | +package com.huaheng.framework.redis.serializer; | |
2 | + | |
3 | +import org.slf4j.Logger; | |
4 | +import org.slf4j.LoggerFactory; | |
5 | +import org.springframework.data.redis.serializer.RedisSerializer; | |
6 | +import org.springframework.data.redis.serializer.SerializationException; | |
7 | + | |
8 | +import java.io.*; | |
9 | + | |
10 | +public class SerializeUtils<T> implements RedisSerializer<T> | |
11 | +{ | |
12 | + private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class); | |
13 | + | |
14 | + public static boolean isEmpty(byte[] data) | |
15 | + { | |
16 | + return (data == null || data.length == 0); | |
17 | + } | |
18 | + | |
19 | + /** | |
20 | + * 序列化 | |
21 | + * | |
22 | + * @param t | |
23 | + * @return | |
24 | + * @throws SerializationException | |
25 | + */ | |
26 | + @Override | |
27 | + public byte[] serialize(T t) throws SerializationException | |
28 | + { | |
29 | + byte[] result = null; | |
30 | + | |
31 | + if (t == null) | |
32 | + { | |
33 | + return new byte[0]; | |
34 | + } | |
35 | + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128); | |
36 | + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream)) | |
37 | + { | |
38 | + | |
39 | + if (!(t instanceof Serializable)) | |
40 | + { | |
41 | + throw new IllegalArgumentException( | |
42 | + SerializeUtils.class.getSimpleName() + " requires a Serializable payload " | |
43 | + + "but received an object of type [" + t.getClass().getName() + "]"); | |
44 | + } | |
45 | + | |
46 | + objectOutputStream.writeObject(t); | |
47 | + objectOutputStream.flush(); | |
48 | + result = byteStream.toByteArray(); | |
49 | + } | |
50 | + catch (Exception ex) | |
51 | + { | |
52 | + logger.error("Failed to serialize", ex); | |
53 | + } | |
54 | + return result; | |
55 | + } | |
56 | + | |
57 | + /** | |
58 | + * 反序列化 | |
59 | + * | |
60 | + * @param bytes | |
61 | + * @return | |
62 | + * @throws SerializationException | |
63 | + */ | |
64 | + @SuppressWarnings("unchecked") | |
65 | + @Override | |
66 | + public T deserialize(byte[] bytes) throws SerializationException | |
67 | + { | |
68 | + T result = null; | |
69 | + if (isEmpty(bytes)) | |
70 | + { | |
71 | + return null; | |
72 | + } | |
73 | + ObjectInputStream objectInputStream; | |
74 | + try | |
75 | + { | |
76 | + objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes)); | |
77 | + result = (T) objectInputStream.readObject(); | |
78 | + } | |
79 | + catch (Exception e) | |
80 | + { | |
81 | + logger.error("Failed to deserialize", e); | |
82 | + } | |
83 | + return result; | |
84 | + } | |
85 | +} | |
... | ... |
src/main/java/com/huaheng/framework/shiro/service/PasswordService.java
... | ... | @@ -2,6 +2,9 @@ package com.huaheng.framework.shiro.service; |
2 | 2 | |
3 | 3 | import java.util.concurrent.atomic.AtomicInteger; |
4 | 4 | import javax.annotation.PostConstruct; |
5 | + | |
6 | +import com.huaheng.common.exception.user.UserPasswordRetryLimitCountException; | |
7 | +import com.huaheng.common.utils.RedisUtils; | |
5 | 8 | import org.apache.shiro.cache.Cache; |
6 | 9 | import org.apache.shiro.cache.CacheManager; |
7 | 10 | import org.apache.shiro.crypto.hash.Md5Hash; |
... | ... | @@ -17,74 +20,65 @@ import com.huaheng.pc.system.user.domain.User; |
17 | 20 | |
18 | 21 | /** |
19 | 22 | * 登录密码方法 |
20 | - * | |
23 | + * | |
21 | 24 | * @author huaheng |
22 | 25 | */ |
23 | 26 | @Component |
24 | -public class PasswordService | |
25 | -{ | |
27 | +public class PasswordService { | |
26 | 28 | |
27 | 29 | @Autowired |
28 | - private CacheManager cacheManager; | |
30 | + private RedisUtils redisManager; | |
29 | 31 | |
30 | 32 | private Cache<String, AtomicInteger> loginRecordCache; |
31 | 33 | |
32 | 34 | @Value(value = "${user.password.maxRetryCount}") |
33 | 35 | private String maxRetryCount; |
34 | 36 | |
35 | - @PostConstruct | |
36 | - public void init() | |
37 | - { | |
38 | - loginRecordCache = cacheManager.getCache("loginRecordCache"); | |
37 | + public static final String DEFAULT_RETRYLIMIT_KEY_PREFIX = "shiro:redisCache:retry:limit:"; | |
38 | + private String keyPrefix = DEFAULT_RETRYLIMIT_KEY_PREFIX; | |
39 | + | |
40 | + private String getRedisRetryLimitKey(String username) { | |
41 | + return this.keyPrefix + username; | |
39 | 42 | } |
40 | 43 | |
41 | - public void validate(User user, String password) | |
42 | - { | |
44 | + public void validate(User user, String password) { | |
43 | 45 | String loginName = user.getLoginName(); |
44 | 46 | |
45 | - AtomicInteger retryCount = loginRecordCache.get(loginName); | |
47 | + AtomicInteger retryCount = (AtomicInteger) redisManager.get(true, getRedisRetryLimitKey(loginName)); | |
46 | 48 | |
47 | - if (retryCount == null) | |
48 | - { | |
49 | + if (retryCount == null) { | |
49 | 50 | retryCount = new AtomicInteger(0); |
50 | - loginRecordCache.put(loginName, retryCount); | |
51 | + redisManager.set(true, getRedisRetryLimitKey(loginName), retryCount); | |
51 | 52 | } |
52 | - if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue()) | |
53 | - { | |
53 | + if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue()) { | |
54 | 54 | SystemLogUtils.log(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)); |
55 | + redisManager.set(true, getRedisRetryLimitKey(loginName), retryCount, 1 * 60 * 1000); | |
55 | 56 | throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue()); |
56 | 57 | } |
57 | 58 | |
58 | - if (!matches(user, password)) | |
59 | - { | |
59 | + if (!matches(user, password)) { | |
60 | 60 | SystemLogUtils.log(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount, password)); |
61 | - loginRecordCache.put(loginName, retryCount); | |
62 | - throw new UserPasswordNotMatchException(); | |
63 | - } | |
64 | - else | |
65 | - { | |
61 | + redisManager.set(true, getRedisRetryLimitKey(loginName), retryCount); | |
62 | + throw new UserPasswordRetryLimitCountException(Integer.valueOf(retryCount.incrementAndGet() - 1).intValue()); | |
63 | + } else { | |
66 | 64 | clearLoginRecordCache(loginName); |
67 | 65 | } |
68 | 66 | } |
69 | 67 | |
70 | - public boolean matches(User user, String newPassword) | |
71 | - { | |
68 | + public boolean matches(User user, String newPassword) { | |
72 | 69 | return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt())); |
73 | 70 | } |
74 | 71 | |
75 | - public void clearLoginRecordCache(String username) | |
76 | - { | |
77 | - loginRecordCache.remove(username); | |
72 | + public void clearLoginRecordCache(String username) { | |
73 | + redisManager.del(true, getRedisRetryLimitKey(username)); | |
78 | 74 | } |
79 | 75 | |
80 | - public String encryptPassword(String username, String password, String salt) | |
81 | - { | |
76 | + public String encryptPassword(String username, String password, String salt) { | |
82 | 77 | return new Md5Hash(username + password + salt).toHex().toString(); |
83 | 78 | } |
84 | 79 | |
85 | - public static void main(String[] args) | |
86 | - { | |
87 | - //System.out.println(new PasswordService().encryptPassword("admin", "admin123", "111111")); | |
80 | + public static void main(String[] args) { | |
81 | + System.out.println(new PasswordService().encryptPassword("huaheng", "123456", "c97f29")); | |
88 | 82 | //System.out.println(new PasswordService().encryptPassword("ry", "admin123", "222222")); |
89 | 83 | } |
90 | 84 | } |
... | ... |
src/main/java/com/huaheng/framework/shiro/web/filter/online/OnlineSessionFilter.java
... | ... | @@ -3,6 +3,9 @@ package com.huaheng.framework.shiro.web.filter.online; |
3 | 3 | import java.io.IOException; |
4 | 4 | import javax.servlet.ServletRequest; |
5 | 5 | import javax.servlet.ServletResponse; |
6 | + | |
7 | +import com.huaheng.pc.system.dept.domain.Dept; | |
8 | +import com.huaheng.pc.system.dept.service.IDeptService; | |
6 | 9 | import org.apache.shiro.session.Session; |
7 | 10 | import org.apache.shiro.subject.Subject; |
8 | 11 | import org.apache.shiro.web.filter.AccessControlFilter; |
... | ... | @@ -17,11 +20,10 @@ import com.huaheng.pc.system.user.domain.User; |
17 | 20 | |
18 | 21 | /** |
19 | 22 | * 自定义访问控制 |
20 | - * | |
23 | + * | |
21 | 24 | * @author huaheng |
22 | 25 | */ |
23 | -public class OnlineSessionFilter extends AccessControlFilter | |
24 | -{ | |
26 | +public class OnlineSessionFilter extends AccessControlFilter { | |
25 | 27 | |
26 | 28 | /** |
27 | 29 | * 强制退出后重定向的地址 |
... | ... | @@ -32,39 +34,38 @@ public class OnlineSessionFilter extends AccessControlFilter |
32 | 34 | @Autowired |
33 | 35 | private OnlineSessionDAO onlineSessionDAO; |
34 | 36 | |
37 | + @Autowired | |
38 | + private IDeptService deptService; | |
39 | + | |
35 | 40 | /** |
36 | 41 | * 表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false; |
37 | 42 | */ |
38 | 43 | @Override |
39 | 44 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) |
40 | - throws Exception | |
41 | - { | |
45 | + throws Exception { | |
42 | 46 | Subject subject = getSubject(request, response); |
43 | - if (subject == null || subject.getSession() == null) | |
44 | - { | |
47 | + if (subject == null || subject.getSession() == null) { | |
45 | 48 | return true; |
46 | 49 | } |
47 | 50 | Session session = onlineSessionDAO.readSession(subject.getSession().getId()); |
48 | - if (session != null && session instanceof OnlineSession) | |
49 | - { | |
51 | + if (session != null && session instanceof OnlineSession) { | |
50 | 52 | OnlineSession onlineSession = (OnlineSession) session; |
51 | 53 | request.setAttribute(ShiroConstants.ONLINE_SESSION, onlineSession); |
52 | 54 | // 把user对象设置进去 |
53 | 55 | boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L; |
54 | - if (isGuest == true) | |
55 | - { | |
56 | + if (isGuest == true) { | |
56 | 57 | User user = ShiroUtils.getUser(); |
57 | - if (user != null) | |
58 | - { | |
58 | + if (user != null) { | |
59 | + Integer deptId = user.getDeptId(); | |
60 | + Dept dept = deptService.selectDeptById(deptId); | |
59 | 61 | onlineSession.setUserId(user.getId()); |
60 | 62 | onlineSession.setLoginName(user.getLoginName()); |
61 | - onlineSession.setDeptName(user.getDept().getDeptName()); | |
63 | + onlineSession.setDeptName(dept.getDeptName()); | |
62 | 64 | onlineSession.markAttributeChanged(); |
63 | 65 | } |
64 | 66 | } |
65 | 67 | |
66 | - if (onlineSession.getStatus() == OnlineSession.OnlineStatus.off_line) | |
67 | - { | |
68 | + if (onlineSession.getStatus() == OnlineSession.OnlineStatus.off_line) { | |
68 | 69 | return false; |
69 | 70 | } |
70 | 71 | } |
... | ... | @@ -75,11 +76,9 @@ public class OnlineSessionFilter extends AccessControlFilter |
75 | 76 | * 表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。 |
76 | 77 | */ |
77 | 78 | @Override |
78 | - protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception | |
79 | - { | |
79 | + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { | |
80 | 80 | Subject subject = getSubject(request, response); |
81 | - if (subject != null) | |
82 | - { | |
81 | + if (subject != null) { | |
83 | 82 | subject.logout(); |
84 | 83 | } |
85 | 84 | saveRequestAndRedirectToLogin(request, response); |
... | ... | @@ -88,8 +87,7 @@ public class OnlineSessionFilter extends AccessControlFilter |
88 | 87 | |
89 | 88 | // 跳转到登录页 |
90 | 89 | @Override |
91 | - protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException | |
92 | - { | |
90 | + protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException { | |
93 | 91 | WebUtils.issueRedirect(request, response, loginUrl); |
94 | 92 | } |
95 | 93 | |
... | ... |
src/main/resources/application.yml
... | ... | @@ -9,7 +9,7 @@ huaheng: |
9 | 9 | #版本 |
10 | 10 | version: 4.0.0 |
11 | 11 | #版权年份 |
12 | - copyrightYear: 2022 | |
12 | + copyrightYear: 2024 | |
13 | 13 | # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) |
14 | 14 | profile: D:/Huaheng/uploadPath/ |
15 | 15 | # apk路径 |
... | ... | @@ -106,29 +106,27 @@ spring: |
106 | 106 | password: owobzjvlgsxrbdfe |
107 | 107 | # 编码类型 |
108 | 108 | default-encoding: utf-8 |
109 | - # redis 配置 | |
110 | - redis: | |
111 | - # 地址 | |
112 | - # host: 192.168.100.132 | |
113 | - # host: 192.168.100.134 | |
114 | - # host: 192.168.100.136 | |
115 | - host: localhost | |
116 | - # 端口,默认为6379 | |
117 | - port: 6379 | |
118 | - # 密码 | |
119 | - password: | |
120 | - # 连接超时时间 | |
121 | - timeout: 10s | |
122 | - lettuce: | |
123 | - pool: | |
124 | - # 连接池中的最小空闲连接 | |
125 | - min-idle: 0 | |
126 | - # 连接池中的最大空闲连接 | |
127 | - max-idle: 8 | |
128 | - # 连接池的最大数据库连接数 | |
129 | - max-active: 8 | |
130 | - # #连接池最大阻塞等待时间(使用负值表示没有限制) | |
131 | - max-wait: -1ms | |
109 | + | |
110 | + | |
111 | + | |
112 | +# redis 配置 | |
113 | +redis: | |
114 | + database: 0 | |
115 | + host: localhost | |
116 | + lettuce: | |
117 | + pool: | |
118 | + max-active: 8 #最大连接数据库连接数,设 0 为没有限制 | |
119 | + max-idle: 8 #最大等待连接中的数量,设 0 为没有限制 | |
120 | + max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。 | |
121 | + min-idle: 0 #最小等待连接中的数量,设 0 为没有限制 | |
122 | + max-total: 50 #连接池最大连接数(使用负值表示没有限制) | |
123 | + max-waitMillis: -1 #连接池最大阻塞等待时间(使用负值表示没有限制) | |
124 | + shutdown-timeout: 100ms | |
125 | + password: | |
126 | + #password: | |
127 | + port: 6379 | |
128 | + ssl: true | |
129 | + timeout: 14400000 #4h 4*60*60*1000 | |
132 | 130 | |
133 | 131 | mybatis-plus: |
134 | 132 | mapper-locations: classpath:mybatis/**/*.xml |
... | ... | @@ -168,12 +166,14 @@ shiro: |
168 | 166 | # 设置Cookie的过期时间,天为单位 |
169 | 167 | maxAge: -1 |
170 | 168 | session: |
169 | + #启用redisCache | |
170 | + redisEnabled: true | |
171 | 171 | # Session超时时间(默认30分钟) |
172 | 172 | expireTime: -1 |
173 | 173 | # 同步session到数据库的周期(默认1分钟) |
174 | 174 | dbSyncPeriod: 1 |
175 | 175 | # 相隔多久检查一次session的有效性,默认就是10分钟 |
176 | - validationInterval: -1 | |
176 | + validationInterval: 1000000 | |
177 | 177 | |
178 | 178 | # 防止XSS攻击 |
179 | 179 | xss: |
... | ... |