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;