|
|
@@ -8,6 +8,8 @@ import jakarta.servlet.http.HttpServletResponse;
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
+import org.slf4j.MDC;
|
|
|
+import com.pharmacopoeia.util.IpUtils;
|
|
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
|
import org.springframework.security.core.context.SecurityContextHolder;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
@@ -22,7 +24,9 @@ public class JwtAuthFilter extends OncePerRequestFilter {
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(JwtAuthFilter.class);
|
|
|
|
|
|
- /** 始终公开的路径(不需要 token) */
|
|
|
+ /**
|
|
|
+ * 始终公开的路径(不需要 token)
|
|
|
+ */
|
|
|
private static final Set<String> PUBLIC_PREFIXES = Set.of(
|
|
|
"/health",
|
|
|
"/api/v1/auth/",
|
|
|
@@ -51,74 +55,81 @@ public class JwtAuthFilter extends OncePerRequestFilter {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected void doFilterInternal(HttpServletRequest request,
|
|
|
+ protected void doFilterInternal(@NotNull HttpServletRequest request,
|
|
|
@NotNull HttpServletResponse response,
|
|
|
@NotNull FilterChain filterChain) throws ServletException, IOException {
|
|
|
- String uri = request.getRequestURI();
|
|
|
- String method = request.getMethod();
|
|
|
+ String ip = IpUtils.getClientIp(request);
|
|
|
+ MDC.put("ip", ip);
|
|
|
+ try {
|
|
|
+ String uri = request.getRequestURI();
|
|
|
+ String method = request.getMethod();
|
|
|
|
|
|
- // 公开路径:不要求 token,但如果带来了有效 token 仍解析出用户身份
|
|
|
- if (isPublicPath(uri)) {
|
|
|
- String header = request.getHeader("Authorization");
|
|
|
- if (header != null && header.startsWith("Bearer ")) {
|
|
|
- try {
|
|
|
- String token = header.substring(7);
|
|
|
- if (jwtUtil.validateToken(token)) {
|
|
|
- String subject = jwtUtil.getSubject(token);
|
|
|
- SecurityContextHolder.getContext().setAuthentication(
|
|
|
- new UsernamePasswordAuthenticationToken(subject, null, Collections.emptyList()));
|
|
|
+ // 公开路径:不要求 token,但如果带来了有效 token 仍解析出用户身份
|
|
|
+ if (isPublicPath(uri)) {
|
|
|
+ String header = request.getHeader("Authorization");
|
|
|
+ if (header != null && header.startsWith("Bearer ")) {
|
|
|
+ try {
|
|
|
+ String token = header.substring(7);
|
|
|
+ if (jwtUtil.validateToken(token)) {
|
|
|
+ String subject = jwtUtil.getSubject(token);
|
|
|
+ SecurityContextHolder.getContext().setAuthentication(
|
|
|
+ new UsernamePasswordAuthenticationToken(subject, null, Collections.emptyList()));
|
|
|
+ }
|
|
|
+ } catch (Exception ignored) {
|
|
|
}
|
|
|
- } catch (Exception ignored) {}
|
|
|
- }
|
|
|
- filterChain.doFilter(request, response);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- String header = request.getHeader("Authorization");
|
|
|
-
|
|
|
- if (header != null && header.startsWith("Bearer ")) {
|
|
|
- String token = header.substring(7);
|
|
|
- log.info("JWT 认证请求: {} {} | token={}...", method, uri, token.substring(0, Math.min(20, token.length())));
|
|
|
- if (jwtUtil.validateToken(token)) {
|
|
|
- String subject = jwtUtil.getSubject(token);
|
|
|
- var auth = new UsernamePasswordAuthenticationToken(subject, null, Collections.emptyList());
|
|
|
- SecurityContextHolder.getContext().setAuthentication(auth);
|
|
|
- log.info("JWT 认证通过: subject={}, uri={} {}", subject, method, uri);
|
|
|
+ }
|
|
|
filterChain.doFilter(request, response);
|
|
|
return;
|
|
|
}
|
|
|
- // token 无效
|
|
|
- log.warn("JWT 认证拒绝(token无效): {} {}", method, uri);
|
|
|
- if (authProperties.isEnabled()) {
|
|
|
- send401(response, "token 无效或已过期");
|
|
|
- return;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 无 token
|
|
|
- log.warn("JWT 认证拒绝(无token): {} {}", method, uri);
|
|
|
- if (authProperties.isEnabled()) {
|
|
|
- send401(response, "未提供认证 token");
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- // 鉴权未启用,放行
|
|
|
- log.debug("鉴权未启用,放行: {} {}", method, uri);
|
|
|
- filterChain.doFilter(request, response);
|
|
|
- }
|
|
|
+ String header = request.getHeader("Authorization");
|
|
|
|
|
|
- private boolean isPublicPath(String uri) {
|
|
|
- for (String prefix : PUBLIC_PREFIXES) {
|
|
|
- if (uri.startsWith(prefix)) {
|
|
|
- return true;
|
|
|
+ if (header != null && header.startsWith("Bearer ")) {
|
|
|
+ String token = header.substring(7);
|
|
|
+ log.info("JWT 认证请求: {} {} | token={}...", method, uri, token.substring(0, Math.min(20, token.length())));
|
|
|
+ if (jwtUtil.validateToken(token)) {
|
|
|
+ String subject = jwtUtil.getSubject(token);
|
|
|
+ var auth = new UsernamePasswordAuthenticationToken(subject, null, Collections.emptyList());
|
|
|
+ SecurityContextHolder.getContext().setAuthentication(auth);
|
|
|
+ log.info("JWT 认证通过: subject={}, uri={} {}", subject, method, uri);
|
|
|
+ filterChain.doFilter(request, response);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // token 无效
|
|
|
+ log.warn("JWT 认证拒绝(token无效): {} {}", method, uri);
|
|
|
+ if (authProperties.isEnabled()) {
|
|
|
+ send401(response, "token 无效或已过期");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 无 token
|
|
|
+ log.warn("JWT 认证拒绝(无token): {} {}", method, uri);
|
|
|
+ if (authProperties.isEnabled()) {
|
|
|
+ send401(response, "未提供认证 token");
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // 鉴权未启用,放行
|
|
|
+ log.debug("鉴权未启用,放行: {} {}", method, uri);
|
|
|
+ filterChain.doFilter(request, response);
|
|
|
+ }finally {
|
|
|
+ MDC.clear();
|
|
|
}
|
|
|
- return "/".equals(uri) || uri.endsWith(".html") || uri.endsWith(".js") || uri.endsWith(".css");
|
|
|
- }
|
|
|
+}
|
|
|
|
|
|
- private void send401(HttpServletResponse response, String msg) throws IOException {
|
|
|
- response.setStatus(401);
|
|
|
- response.setContentType("application/json;charset=UTF-8");
|
|
|
- response.getWriter().write("{\"error\":\"" + msg + "\",\"code\":401}");
|
|
|
+private boolean isPublicPath(String uri) {
|
|
|
+ for (String prefix : PUBLIC_PREFIXES) {
|
|
|
+ if (uri.startsWith(prefix)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
+ return "/".equals(uri) || uri.endsWith(".html") || uri.endsWith(".js") || uri.endsWith(".css");
|
|
|
+}
|
|
|
+
|
|
|
+private void send401(HttpServletResponse response, String msg) throws IOException {
|
|
|
+ response.setStatus(401);
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write("{\"error\":\"" + msg + "\",\"code\":401}");
|
|
|
+}
|
|
|
}
|