From 8b6ad5330b5349b9fc0632cf0c4086d1c7e18a87 Mon Sep 17 00:00:00 2001 From: liu Date: Fri, 1 Dec 2023 16:04:09 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90Redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- virtual-patient-common/pom.xml | 9 ++++ .../supervision/config/JwtInterceptor.java | 35 ++++++++++++- .../config/UserSingleLoginConfig.java | 50 ------------------- .../com/supervision/config/WebConfig.java | 7 ++- .../constant/UserTokenConstant.java | 6 +++ .../java/com/supervision/util/TokenUtil.java | 1 + .../src/main/resources/application-dev.yml | 4 ++ .../src/main/resources/application-prod.yml | 4 ++ .../src/main/resources/application-test.yml | 4 ++ .../controller/UserController.java | 18 ++++--- .../src/main/resources/application-dev.yml | 5 ++ .../src/main/resources/application-prod.yml | 4 ++ .../src/main/resources/application-test.yml | 4 ++ 13 files changed, 91 insertions(+), 60 deletions(-) delete mode 100644 virtual-patient-common/src/main/java/com/supervision/config/UserSingleLoginConfig.java create mode 100644 virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java diff --git a/virtual-patient-common/pom.xml b/virtual-patient-common/pom.xml index f666a501..39517bdf 100644 --- a/virtual-patient-common/pom.xml +++ b/virtual-patient-common/pom.xml @@ -30,6 +30,15 @@ + + org.springframework.boot + spring-boot-starter-data-redis + + + redis.clients + jedis + + org.springframework.boot diff --git a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java index cd4374d1..8513504d 100644 --- a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java +++ b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java @@ -1,13 +1,17 @@ package com.supervision.config; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; +import com.supervision.constant.UserTokenConstant; import com.supervision.exception.BusinessException; +import com.supervision.util.SpringBeanUtil; import com.supervision.util.TokenUtil; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerInterceptor; @@ -19,10 +23,16 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; @Slf4j public class JwtInterceptor implements HandlerInterceptor { + private final RedisTemplate redisTemplate; + + public JwtInterceptor(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } @Override @@ -43,11 +53,33 @@ public class JwtInterceptor implements HandlerInterceptor { // 校验token是否过期,如果过期了,需要提示过期重新登录 checkTokenExpire(jwt); // 校验是否重复登录 - //UserSingleLoginConfig.checkSingleLogin(jwt); + //checkSingleLogin(jwt); cacheAuth(jwt); return true; } + public void checkSingleLogin(String userId, JWT currentJwt) { + + if (Boolean.FALSE.equals(redisTemplate.hasKey(UserTokenConstant.TOKEN_CACHE + userId))) { + throw new BusinessException("用户已被踢下线或超时,请重新登录", 505); + } + String value = redisTemplate.opsForValue().get(UserTokenConstant.TOKEN_CACHE + userId); + + long redisCacheTime = Long.parseLong(String.valueOf(value)); + Object currentJwtIssueTimeObject = currentJwt.getPayload("issueTime"); + long currentJwtIssueTime = Long.parseLong(String.valueOf(currentJwtIssueTimeObject)); + if (redisCacheTime == currentJwtIssueTime) { + // 如果相等,说明这个token就是最新的,直接放行 + return; + } else if (currentJwtIssueTime > redisCacheTime) { + // 如果当前请求时间,大于Redis缓存时间,说明重新登录了,这个时候要把最新的放到缓存中 + redisTemplate.opsForValue().set(UserTokenConstant.TOKEN_CACHE + userId, String.valueOf(System.currentTimeMillis()), 1000 * 5L, TimeUnit.MILLISECONDS); + } else { + // 走到这里,说明redisCacheTime是最新的,说明当前用户请求了一个新的token,那么原来的用户就踢掉 + throw new BusinessException("当前用户已在其他地方登录!", 505); + } + } + @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { @@ -68,7 +100,6 @@ public class JwtInterceptor implements HandlerInterceptor { } - private void cacheAuth(JWT jwt) { try { JSONObject claimsJson = jwt.getPayload().getClaimsJson(); diff --git a/virtual-patient-common/src/main/java/com/supervision/config/UserSingleLoginConfig.java b/virtual-patient-common/src/main/java/com/supervision/config/UserSingleLoginConfig.java deleted file mode 100644 index c3f0f837..00000000 --- a/virtual-patient-common/src/main/java/com/supervision/config/UserSingleLoginConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.supervision.config; - -import cn.hutool.cache.CacheUtil; -import cn.hutool.cache.impl.TimedCache; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.jwt.JWT; -import com.supervision.exception.BusinessException; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class UserSingleLoginConfig { - - /** - * 创建缓存,过期时间为5分钟,如果5分钟内没有请求过来,就认为是超时 - */ - private static final TimedCache singleLoginTokenCacheMap = CacheUtil.newTimedCache(1000 * 5 * 24); - - static { - // 每秒钟扫描一次 - singleLoginTokenCacheMap.schedulePrune(1000); - } - - - public static void loginOrRefreshUser(String id, JWT jwt) { - singleLoginTokenCacheMap.put(id, jwt); - } - - public static void checkSingleLogin(JWT currentJwt) { - Object id = currentJwt.getPayload("id"); - JWT singleLoginTokenCache = singleLoginTokenCacheMap.get(id); - if (ObjectUtil.isEmpty(singleLoginTokenCache)) { - throw new BusinessException("用户已被踢下线或超时,请重新登录", 505); - } - // 然后将当前的expireTime和singleLoginTokenCache进行比较 - Object expireTime = singleLoginTokenCache.getPayload("expireTime"); - long singleLoginTokenCacheExpireTime = Long.parseLong(String.valueOf(expireTime)); - Object currentJwtExpireTimeObject = currentJwt.getPayload("expireTime"); - long currentJwtExpireTime = Long.parseLong(String.valueOf(currentJwtExpireTimeObject)); - if (singleLoginTokenCacheExpireTime == currentJwtExpireTime) { - // 如果相等,说明这个token就是最新的,直接放行 - return; - } else if (currentJwtExpireTime > singleLoginTokenCacheExpireTime) { - // 如果当前的超时时间要大于缓存的,说明重新登录了,这个时候要把最新的放到缓存中 - singleLoginTokenCacheMap.put(id, currentJwt); - } else { - // 走到这里,说明singleLoginTokenCache是最新的,说明当前用户请求了一个新的token,那么原来的用户就踢掉 - throw new BusinessException("当前用户已在其他地方登录!", 505); - } - } -} diff --git a/virtual-patient-common/src/main/java/com/supervision/config/WebConfig.java b/virtual-patient-common/src/main/java/com/supervision/config/WebConfig.java index a468d508..80e72538 100644 --- a/virtual-patient-common/src/main/java/com/supervision/config/WebConfig.java +++ b/virtual-patient-common/src/main/java/com/supervision/config/WebConfig.java @@ -5,8 +5,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -18,10 +20,13 @@ import java.util.List; @Configuration public class WebConfig implements WebMvcConfigurer { + @Autowired + private RedisTemplate redisTemplate; + @Override public void addInterceptors(InterceptorRegistry registry) { // 添加权限拦截器 - registry.addInterceptor(new JwtInterceptor()) + registry.addInterceptor(new JwtInterceptor(redisTemplate)) .addPathPatterns("/**") .excludePathPatterns(ignorePathPatterns()); } diff --git a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java new file mode 100644 index 00000000..a6e5d2fb --- /dev/null +++ b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java @@ -0,0 +1,6 @@ +package com.supervision.constant; + +public interface UserTokenConstant { + + String TOKEN_CACHE = "USER:LOGIN:TOKEN:"; +} diff --git a/virtual-patient-common/src/main/java/com/supervision/util/TokenUtil.java b/virtual-patient-common/src/main/java/com/supervision/util/TokenUtil.java index b33841ca..1a0b0bf0 100644 --- a/virtual-patient-common/src/main/java/com/supervision/util/TokenUtil.java +++ b/virtual-patient-common/src/main/java/com/supervision/util/TokenUtil.java @@ -13,6 +13,7 @@ public class TokenUtil { JSONObject info = JSONUtil.parseObj(userInfo); // 过期时间一天,同时这个字段也作为单点登录使用 info.putOnce("expireTime",System.currentTimeMillis() + 1000 * 60 * 60 * 24); + info.putOnce("issueTime",System.currentTimeMillis()); return JWTUtil.createToken(info, signer); } } diff --git a/virtual-patient-manage/src/main/resources/application-dev.yml b/virtual-patient-manage/src/main/resources/application-dev.yml index c697021a..a6ba84a7 100644 --- a/virtual-patient-manage/src/main/resources/application-dev.yml +++ b/virtual-patient-manage/src/main/resources/application-dev.yml @@ -46,6 +46,10 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml diff --git a/virtual-patient-manage/src/main/resources/application-prod.yml b/virtual-patient-manage/src/main/resources/application-prod.yml index d09b3af7..cd7989fa 100644 --- a/virtual-patient-manage/src/main/resources/application-prod.yml +++ b/virtual-patient-manage/src/main/resources/application-prod.yml @@ -47,6 +47,10 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml diff --git a/virtual-patient-manage/src/main/resources/application-test.yml b/virtual-patient-manage/src/main/resources/application-test.yml index a7a01804..f9335324 100644 --- a/virtual-patient-manage/src/main/resources/application-test.yml +++ b/virtual-patient-manage/src/main/resources/application-test.yml @@ -47,6 +47,10 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java index a1a11e6a..3eb554a4 100644 --- a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java +++ b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java @@ -2,9 +2,7 @@ package com.supervision.controller; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import cn.hutool.jwt.JWT; -import com.supervision.config.UserSingleLoginConfig; -import com.supervision.domain.UserInfo; +import com.supervision.constant.UserTokenConstant; import com.supervision.exception.BusinessException; import com.supervision.model.User; import com.supervision.pojo.vo.LoginReqVO; @@ -14,9 +12,11 @@ import com.supervision.util.UserUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import java.util.Optional; +import java.util.concurrent.TimeUnit; @Api(tags = "用户管理") @RestController @@ -26,6 +26,9 @@ public class UserController { private final UserService userService; + private final RedisTemplate redisTemplate; + + @ApiOperation("登录") @PostMapping("login") public String login(@RequestBody LoginReqVO reqVO) { @@ -40,16 +43,17 @@ public class UserController { throw new BusinessException("密码错误"); } String token = TokenUtil.creatToken(JSONUtil.toJsonStr(user.get())); - // 将用户的token保存起来 - UserSingleLoginConfig.loginOrRefreshUser(user.get().getId(), JWT.create().parse(token)); + // 将用户的token保存起来,超时时间为5分钟 + redisTemplate.opsForValue().set(UserTokenConstant.TOKEN_CACHE + user.get().getId(), String.valueOf(System.currentTimeMillis()), 1000 * 5L, TimeUnit.MILLISECONDS); return token; } @ApiOperation("token心跳") @PostMapping("keepaliveToken") public void keepaliveToken() { - String token = UserUtil.getUserToken(); - UserSingleLoginConfig.loginOrRefreshUser(UserUtil.getUser().getId(), JWT.create().parse(token)); + User user = UserUtil.getUser(); + // 每次心跳都设置为5分钟之后 + redisTemplate.expire(UserTokenConstant.TOKEN_CACHE + user.getId(), 1000 * 5L, TimeUnit.MILLISECONDS); } diff --git a/virtual-patient-web/src/main/resources/application-dev.yml b/virtual-patient-web/src/main/resources/application-dev.yml index 0750ca75..3778db98 100644 --- a/virtual-patient-web/src/main/resources/application-dev.yml +++ b/virtual-patient-web/src/main/resources/application-dev.yml @@ -46,6 +46,11 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 + mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml diff --git a/virtual-patient-web/src/main/resources/application-prod.yml b/virtual-patient-web/src/main/resources/application-prod.yml index 84d6c7b9..06592d11 100644 --- a/virtual-patient-web/src/main/resources/application-prod.yml +++ b/virtual-patient-web/src/main/resources/application-prod.yml @@ -47,6 +47,10 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml diff --git a/virtual-patient-web/src/main/resources/application-test.yml b/virtual-patient-web/src/main/resources/application-test.yml index 2632bb2a..45c38e68 100644 --- a/virtual-patient-web/src/main/resources/application-test.yml +++ b/virtual-patient-web/src/main/resources/application-test.yml @@ -47,6 +47,10 @@ spring: log-slow-sql: true # 是否开启 慢SQL 记录,默认false slow-sql-millis: 5000 # 慢 SQL 的标准,默认 3000,单位:毫秒 merge-sql: false # 合并多个连接池的监控数据,默认false + redis: + host: 192.168.10.138 + port: 6379 + password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml