修改框架登录认证逻辑

This commit is contained in:
zk
2026-03-21 16:45:45 +08:00
parent 004464471d
commit 5a40af1f01
@@ -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过滤器
* <p>所有请求先尝试解析token设置认证信息,再根据接口是否公开决定放行或拦截</p>
*
* @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并设置认证信息
* <p>1. 提取token 2. JWT解码 3. Redis校验 4. 设备有效性过滤 5. 续期 6. 设置SecurityContext和MDC</p>
*
* @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<RedisLoginTokenInfo.LoginDevice> devices = info.getLoginDevices().stream().filter(v -> v.getLastLoginTime().plusSeconds(tokenExceedTime).isAfter(Instant.now())).collect(Collectors.toList());
Map<String, RedisLoginTokenInfo.LoginDevice> 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<RedisLoginTokenInfo.LoginDevice> devices = info.getLoginDevices();
// 过滤过期
devices = devices.stream().filter(v -> v.getLastLoginTime().isBefore(new Date(System.currentTimeMillis() + tokenExceedTime * 1000L).toInstant())).collect(Collectors.toList());
Map<String, RedisLoginTokenInfo.LoginDevice> 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<String> 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;