用户登录
校验token,并解析token获取用户信息,并把用户信息存入redis中,分别存入两种形式;
一种是以key-value字符串形式存入redis,一种是以有序集合zset形式存入redis;
前一种便于查看当前用户是否已登录,后一种便于管理人员查看当前在线用户并执行相关操作
| @Override |
| public Map login(JwtAuthenticationRequest authenticationRequest) throws Exception { |
| |
| UserInfo info = permissionService.validate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); |
| if (!StringUtils.isEmpty(info.getId())) { |
| JWTInfo jwtInfo = new JWTInfo(info.getUsername(), info.getId() + "", info.getName()); |
| |
| String token = jwtTokenUtil.generateToken(jwtInfo); |
| Map<String, String> result = new HashMap<>(); |
| result.put("accessToken", token); |
| result.put("id", info.id); |
| |
| writeOnlineLog(jwtInfo); |
| return result; |
| } |
| throw new UserInvalidException("用户不存在或账户密码错误!"); |
| } |
| |
| |
| |
| |
| @Async |
| public void writeOnlineLog(JWTInfo jwtInfo) { |
| final UserAgent userAgent = UserAgent.parseUserAgentString(WebUtils.getRequest().getHeader("User-Agent")); |
| final String ip = IpUtils.getRemoteIP(WebUtils.getRequest()); |
| String address = AddressUtils.getRealAddressByIP(ip); |
| OnlineLog onlineLog = new OnlineLog(); |
| String os = userAgent.getOperatingSystem().getName(); |
| String browser = userAgent.getBrowser().getName(); |
| onlineLog.setBrowser(browser); |
| onlineLog.setIpaddr(ip); |
| onlineLog.setTokenId(jwtInfo.getTokenId()); |
| onlineLog.setLoginTime(System.currentTimeMillis()); |
| onlineLog.setUserId(jwtInfo.getId()); |
| onlineLog.setUserName(jwtInfo.getName()); |
| onlineLog.setLoginLocation(address); |
| onlineLog.setOs(os); |
| |
| stringRedisTemplate.opsForValue().set(RedisKeyConstant.REDIS_KEY_TOKEN + ":" + jwtInfo.getTokenId(), JSON.toJSONString(onlineLog, false), expire, TimeUnit.MINUTES); |
| |
| stringRedisTemplate.opsForZSet().add((RedisKeyConstant.REDIS_KEY_TOKEN), jwtInfo.getTokenId(), 0); |
| } |
zset 不允许重复的成员。zset 的每个元素都会关联一个分数(分数可以重复),redis 通过分数来为集合中的成员进行从小到大的排序
请求过滤器校验
| public class AccessGatewayFilter implements GlobalFilter { |
| |
| private IJWTInfo getJWTUser(ServerHttpRequest request, ServerHttpRequest.Builder ctx) throws Exception { |
| |
| List<String> strings = request.getHeaders().get(userAuthConfig.getTokenHeader()); |
| String authToken = null; |
| if (strings != null) { |
| authToken = strings.get(0); |
| } |
| if (StringUtils.isBlank(authToken)) { |
| strings = request.getQueryParams().get("token"); |
| if (strings != null) { |
| authToken = strings.get(0); |
| } |
| } |
| IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(authToken); |
| |
| String s = stringRedisTemplate.opsForValue().get(RedisKeyConstant.REDIS_KEY_TOKEN + ":" + infoFromToken.getTokenId()); |
| if (StringUtils.isBlank(s)) { |
| |
| throw new UserTokenException("User token expired!"); |
| } |
| |
| ctx.header(userAuthConfig.getTokenHeader(), authToken); |
| BaseContextHandler.setToken(authToken); |
| return infoFromToken; |
| } |
查询用户登录状态/强制下线
| @RestController |
| @RequestMapping("online") |
| public class OnlineController { |
| |
| @Autowired |
| private StringRedisTemplate stringRedisTemplate; |
| |
| |
| |
| |
| |
| |
| |
| @RequestMapping("/page") |
| public TableResultResponse<OnlineLog> getOnlineInfo(@RequestParam(defaultValue = "10") int limit, @RequestParam(defaultValue = "1") int offset) { |
| stringRedisTemplate.opsForValue(); |
| |
| Set<String> ids = stringRedisTemplate.opsForZSet().reverseRange(RedisKeyConstant.REDIS_KEY_TOKEN, (offset - 1) * limit, (offset - 1) * limit + limit - 1); |
| List<OnlineLog> logs = new ArrayList<>(ids.size()); |
| for (String id : ids) { |
| String s = stringRedisTemplate.opsForValue().get(RedisKeyConstant.REDIS_KEY_TOKEN + ":" + id); |
| if (s == null) { |
| stringRedisTemplate.opsForZSet().remove(RedisKeyConstant.REDIS_KEY_TOKEN, id); |
| } else { |
| logs.add(JSON.parseObject(s, OnlineLog.class)); |
| } |
| } |
| return new TableResultResponse<>(stringRedisTemplate.opsForZSet().size(RedisKeyConstant.REDIS_KEY_TOKEN), logs); |
| } |
| |
| |
| |
| |
| |
| |
| @RequestMapping("/{id}") |
| public ObjectRestResponse forceLogout(@PathVariable("id") String tokenId) { |
| |
| stringRedisTemplate.delete(RedisKeyConstant.REDIS_KEY_TOKEN + ":" + tokenId); |
| stringRedisTemplate.opsForZSet().remove(RedisKeyConstant.REDIS_KEY_TOKEN, tokenId); |
| return new ObjectRestResponse<>(); |
| } |
| |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异