From 5a40af1f0144201639bd4da47453d914c91ec4db Mon Sep 17 00:00:00 2001 From: zk Date: Sat, 21 Mar 2026 16:45:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A1=86=E6=9E=B6=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=AE=A4=E8=AF=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JwtAuthenticationTokenFilter.java | 148 +++++++++--------- 1 file changed, 71 insertions(+), 77 deletions(-) diff --git a/common/src/main/java/org/jiayunet/interceptor/JwtAuthenticationTokenFilter.java b/common/src/main/java/org/jiayunet/interceptor/JwtAuthenticationTokenFilter.java index 3570c8f..6822174 100644 --- a/common/src/main/java/org/jiayunet/interceptor/JwtAuthenticationTokenFilter.java +++ b/common/src/main/java/org/jiayunet/interceptor/JwtAuthenticationTokenFilter.java @@ -3,7 +3,6 @@ package org.jiayunet.interceptor; import java.io.IOException; import java.time.Instant; import java.util.Arrays; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -37,6 +36,7 @@ import org.jiayunet.tool.server.RedisServerTool; /** * jwt过滤器 + *

所有请求先尝试解析token设置认证信息,再根据接口是否公开决定放行或拦截

* * @author zk */ @@ -44,26 +44,19 @@ import org.jiayunet.tool.server.RedisServerTool; @Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { - /** - * 加密密钥 - */ + /** 加密密钥 */ @Value("${app.secret.token:youweiqingnian123}") private String secret; - /** - * token过期时间 - */ + /** token过期时间(秒) */ @Value("${app.login.token.exceed_time:43200}") private int tokenExceedTime; - /** - * 忽略请求路径 - */ + + /** 忽略请求路径 */ @Value("${app.ignore.urls}") private String ignoreUrls; - /** - * 全局配置路径 - */ + /** 全局配置路径 */ @Value("${server.servlet.context-path}") private String contextPath; @@ -72,77 +65,83 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 1. 无条件尝试解析token + boolean authenticated = tryAuthenticate(request); - // 忽略接口放行 + // 2. 公开接口直接放行,不管token状态 if (ifCurrentUrl(ignoreUrls, request.getRequestURI())) { filterChain.doFilter(request, response); return; } - // 获取token:优先从cookie获取,不存在则从请求头获取 - String token = null; + // 3. 非公开接口,认证失败则拦截 + Assert.isTrue(authenticated, "用户未登录或登录过期"); + filterChain.doFilter(request, response); + } + + /** + * 尝试解析token并设置认证信息 + *

1. 提取token 2. JWT解码 3. Redis校验 4. 设备有效性过滤 5. 续期 6. 设置SecurityContext和MDC

+ * + * @return true=认证成功 false=认证失败 + */ + private boolean tryAuthenticate(HttpServletRequest request) { + String token = resolveToken(request); + if (!StringUtils.hasText(token)) { + return false; + } + + try { + Algorithm algorithm = Algorithm.HMAC256(secret); + DecodedJWT decodedJwt = JWT.require(algorithm).build().verify(token); + Long userId = decodedJwt.getClaim("userId").asLong(); + String uuId = decodedJwt.getClaim("uuId").asString(); + + // Redis校验 + String redisKey = PreRedisKeyName.LOGIN_TOKEN + userId; + RedisLoginTokenInfo info = redisServerTool.get(redisKey, RedisLoginTokenInfo.class); + if (info == null) { + return false; + } + + // 过滤过期设备 + List devices = info.getLoginDevices().stream().filter(v -> v.getLastLoginTime().plusSeconds(tokenExceedTime).isAfter(Instant.now())).collect(Collectors.toList()); + Map map = devices.stream().collect(Collectors.toMap(RedisLoginTokenInfo.LoginDevice::getUuId, v -> v)); + if (!map.containsKey(uuId)) { + return false; + } + + // 续期 + map.get(uuId).setLastLoginTime(Instant.now()); + info.setLoginDevices(devices); + redisServerTool.set(redisKey, info, tokenExceedTime, TimeUnit.SECONDS); + + // 设置SecurityContext + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(info.getUserId(), info, info.getAuthority().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())); + authenticationToken.setDetails(info); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + + MDC.put("userId", userId.toString()); + return true; + + } catch (Exception e) { + log.debug("Token解析失败: {}", e.getMessage()); + return false; + } + } + + /** + * 从请求中提取token,优先cookie,其次请求头 + */ + private String resolveToken(HttpServletRequest request) { if (request.getCookies() != null) { for (Cookie cookie : request.getCookies()) { if ("Token".equals(cookie.getName())) { - token = cookie.getValue(); - break; + return cookie.getValue(); } } } - - if (!StringUtils.hasText(token)) { - token = request.getHeader("Token"); - } - - - - if (!StringUtils.hasText(token)) { - // 放行 - filterChain.doFilter(request, response); - return; - } - - - // 验证token - Algorithm algorithm = Algorithm.HMAC256(secret); - DecodedJWT decodedJwt = JWT.require(algorithm).build().verify(token); - Long userId = decodedJwt.getClaim("userId").asLong(); - String uuId = decodedJwt.getClaim("uuId").asString(); - - // 获取redis信息 - String redisKey = PreRedisKeyName.LOGIN_TOKEN + userId; - RedisLoginTokenInfo info = null; - info = redisServerTool.get(redisKey, RedisLoginTokenInfo.class); - Assert.notNull(info, "用户未登录"); - - - // 判断登录有效 - - // 获取登录设备信息 - List devices = info.getLoginDevices(); - - // 过滤过期 - devices = devices.stream().filter(v -> v.getLastLoginTime().isBefore(new Date(System.currentTimeMillis() + tokenExceedTime * 1000L).toInstant())).collect(Collectors.toList()); - - Map map = devices.stream().collect(Collectors.toMap(RedisLoginTokenInfo.LoginDevice::getUuId, v -> v)); - Assert.isTrue(map.containsKey(uuId), "登录过期"); - - // 续期时间 - map.get(uuId).setLastLoginTime(Instant.now()); - info.setLoginDevices(devices); - redisServerTool.set(redisKey, info, tokenExceedTime, TimeUnit.SECONDS); - - // 登录 - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(info.getUserId(), info, - info.getAuthority().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())); - authenticationToken.setDetails(info); - - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - - MDC.put("userId", userId.toString()); - - filterChain.doFilter(request, response); + return request.getHeader("Token"); } /** @@ -157,18 +156,13 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { if (!StringUtils.hasText(ignoreUrls)) { return false; } - - // 处理本次请求路径 剔除全局路径 if (!StringUtils.hasText(currentUrl)) { return true; } if (StringUtils.hasText(contextPath)) { currentUrl = currentUrl.replaceFirst(contextPath, "").replaceAll(charToRemove, ""); } - - // 处理需要被忽略的字段 List urls = Arrays.stream(ignoreUrls.split(",")).map(item -> item.replaceAll(charToRemove, "")).filter(item -> !item.isEmpty()).distinct().collect(Collectors.toList()); - for (String str : urls) { if (currentUrl.startsWith(str)) { return true;