Ver Fonte

pms 30分钟不操作token失效

zhangcl há 2 dias atrás
pai
commit
b6f67cc47c

+ 9 - 2
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java

@@ -21,6 +21,8 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Token 过滤器,验证 token 的有效性
@@ -32,7 +34,9 @@ import java.io.IOException;
 public class TokenAuthenticationFilter extends OncePerRequestFilter {
 
     // 需要排除Token续期的接口路径
-    private static final String EXCLUDED_API_PATH = "/admin-api/system/notify-message/get-unread-count";
+    private static final List<String> EXCLUDE_REFRESH_URLS = Arrays.asList(
+            "/admin-api/system/notify-message/get-unread-count"
+    );
 
     private final SecurityProperties securityProperties;
 
@@ -56,7 +60,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
                     loginUser = mockLoginUser(request, token, userType);
                 }
                 // 延长双 token 过期时间 延长 30分钟
-                renewTokenExpireTime(token);
+                String requestURI = request.getRequestURI();
+                if (!EXCLUDE_REFRESH_URLS.contains(requestURI)) {
+                    renewTokenExpireTime(token);
+                }
                 // 2. 设置当前用户
                 if (loginUser != null) {
                     SecurityFrameworkUtils.setLoginUser(loginUser, request);

+ 9 - 3
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java

@@ -51,10 +51,16 @@ public class OAuth2AccessTokenRedisDAO {
      * 续期 accessToken refreshToken
      * @param accessToken
      */
-    public void renewToken(String accessToken, String refreshToken) {
+    public void renewToken(String accessToken, String tokenJson, Long newTtl) {
         String redisKey = formatKey(accessToken);
-        stringRedisTemplate.expire(redisKey, 30, TimeUnit.MINUTES);
-        // stringRedisTemplate.delete(redisKey);
+        stringRedisTemplate.expire(redisKey, newTtl, TimeUnit.SECONDS);
+
+        stringRedisTemplate.opsForValue().set(
+                redisKey,
+                tokenJson,
+                newTtl,
+                TimeUnit.SECONDS
+        );
     }
 
     public void deleteList(Collection<String> accessTokens) {

+ 31 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java

@@ -1,14 +1,17 @@
 package cn.iocoder.yudao.module.system.service.oauth2;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
@@ -28,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -159,10 +163,35 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void renewTokenExpireTime(String accessToken) {
         OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
-        String refreshToken = accessTokenDO.getRefreshToken();
-        oauth2AccessTokenRedisDAO.renewToken(accessToken, refreshToken);
+        if (accessTokenDO == null) {
+            throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在");
+        }
+
+        if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
+            throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期");
+        }
+        LocalDateTime currentExpiresTime = accessTokenDO.getExpiresTime();
+        if (ObjUtil.isEmpty(currentExpiresTime)) {
+            throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "访问令牌过期时间异常");
+        }
+        LocalDateTime currentTime = LocalDateTime.now();
+        // 计算新的过期时间(例如延长30分钟) 以当前时间为基准计算新的过期时间
+        LocalDateTime newExpiresTime = currentTime.plusMinutes(30);
+        long newTtl = LocalDateTimeUtil.between(LocalDateTime.now(), newExpiresTime, ChronoUnit.SECONDS);
+        if (newTtl > 0) {
+            accessTokenDO.setExpiresTime(newExpiresTime);
+            // 只有当accessTokenDO有id时才更新MySQL(避免从refreshToken转换而来的token)
+            if (ObjUtil.isNotEmpty(accessTokenDO.getId())) {
+                oauth2AccessTokenMapper.updateById(accessTokenDO);
+            }
+            // 更新Redis缓存
+            oauth2AccessTokenRedisDAO.set(accessTokenDO);
+        } else {
+            throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "续期失败,新的过期时间无效");
+        }
     }
 
     private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {