From 5dc83a000193cdbff093a8181732393f2378318b Mon Sep 17 00:00:00 2001 From: "Yangkai.Shen" <237497819@qq.com> Date: Wed, 12 Dec 2018 02:03:03 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20spring-boot-demo-rbac-security=20?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-boot-demo-rbac-security/README.md | 132 ++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/spring-boot-demo-rbac-security/README.md b/spring-boot-demo-rbac-security/README.md index 89f66a0..e4eb9f4 100644 --- a/spring-boot-demo-rbac-security/README.md +++ b/spring-boot-demo-rbac-security/README.md @@ -9,7 +9,7 @@ - [x] **登录 / 登出**部分均使用自定义 Controller 实现,未使用 `Spring Security` 内部实现部分,适用于前后端分离项目,详情参考 [`SecurityConfig.java`](./src/main/java/com/xkcoding/rbac/security/config/SecurityConfig.java) 和 [`AuthController.java`](./src/main/java/com/xkcoding/rbac/security/config/AuthController.java) - [x] 持久化技术使用 `spring-data-jpa` 完成 - [x] 使用 `JWT` 实现安全验证,同时引入 `Redis` 解决 `JWT` 无法手动设置过期的弊端,并且保证同一用户在同一时间仅支持同一设备登录,不同设备登录会将,详情参考 [`JwtUtil.java`](./src/main/java/com/xkcoding/rbac/security/config/JwtUtil.java) -- [ ] 在线人数统计 +- [x] 在线人数统计 - [ ] 手动踢出用户 ## 2. 运行 @@ -553,7 +553,135 @@ public class CustomUserDetailsService implements UserDetailsService { } ``` -### 3.7. 其余代码参见本 demo +### 3.7. RedisUtil.java + +> 主要功能:根据key的格式分页获取Redis存在的key列表 + +```java +/** + *

+ * Redis工具类 + *

+ * + * @package: com.xkcoding.rbac.security.util + * @description: Redis工具类 + * @author: yangkai.shen + * @date: Created in 2018-12-11 20:24 + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Component +@Slf4j +public class RedisUtil { + @Autowired + private StringRedisTemplate stringRedisTemplate; + + /** + * 分页获取指定格式key,使用 scan 命令代替 keys 命令,在大数据量的情况下可以提高查询效率 + * + * @param patternKey key格式 + * @param currentPage 当前页码 + * @param pageSize 每页条数 + * @return 分页获取指定格式key + */ + public PageResult findKeysForPage(String patternKey, int currentPage, int pageSize) { + ScanOptions options = ScanOptions.scanOptions() + .match(patternKey) + .build(); + RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory(); + RedisConnection rc = factory.getConnection(); + Cursor cursor = rc.scan(options); + + List result = Lists.newArrayList(); + + long tmpIndex = 0; + int startIndex = (currentPage - 1) * pageSize; + int end = currentPage * pageSize; + while (cursor.hasNext()) { + String key = new String(cursor.next()); + if (tmpIndex >= startIndex && tmpIndex < end) { + result.add(key); + } + tmpIndex++; + } + + try { + cursor.close(); + RedisConnectionUtils.releaseConnection(rc, factory); + } catch (Exception e) { + log.warn("Redis连接关闭异常,", e); + } + + return new PageResult<>(result, tmpIndex); + } +} +``` + +### 3.8. MonitorService.java + +> 监控服务,主要功能:查询当前在线人数分页列表,手动踢出某个用户 + +```java +package com.xkcoding.rbac.security.service; + +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.Lists; +import com.xkcoding.rbac.security.common.Consts; +import com.xkcoding.rbac.security.common.PageResult; +import com.xkcoding.rbac.security.model.User; +import com.xkcoding.rbac.security.repository.UserDao; +import com.xkcoding.rbac.security.util.RedisUtil; +import com.xkcoding.rbac.security.vo.OnlineUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 监控 Service + *

+ * + * @package: com.xkcoding.rbac.security.service + * @description: 监控 Service + * @author: yangkai.shen + * @date: Created in 2018-12-12 00:55 + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Service +public class MonitorService { + @Autowired + private RedisUtil redisUtil; + + @Autowired + private UserDao userDao; + + public PageResult onlineUser(Integer page, Integer size) { + PageResult keys = redisUtil.findKeysForPage(Consts.REDIS_JWT_KEY_PREFIX + Consts.SYMBOL_STAR, page, size); + List rows = keys.getRows(); + Long total = keys.getTotal(); + + // 根据 redis 中键获取用户名列表 + List usernameList = rows.stream() + .map(s -> StrUtil.subAfter(s, Consts.REDIS_JWT_KEY_PREFIX, true)) + .collect(Collectors.toList()); + // 根据用户名查询用户信息 + List userList = userDao.findByUsernameIn(usernameList); + + // 封装在线用户信息 + List onlineUserList = Lists.newArrayList(); + userList.forEach(user -> onlineUserList.add(OnlineUser.create(user))); + + return new PageResult<>(onlineUserList, total); + } +} +``` + +### 3.9. 其余代码参见本 demo ## 4. 参考