liuchengsen 1 ماه پیش
والد
کامیت
79d433a2da

+ 16 - 7
backend-java/src/main/java/com/pharmacopoeia/security/JwtAuthFilter.java

@@ -5,6 +5,8 @@ import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
@@ -17,6 +19,8 @@ import java.util.Set;
 @Component
 public class JwtAuthFilter extends OncePerRequestFilter {
 
+    private static final Logger log = LoggerFactory.getLogger(JwtAuthFilter.class);
+
     /** 始终公开的路径(不需要 token) */
     private static final Set<String> PUBLIC_PREFIXES = Set.of(
             "/health",
@@ -38,6 +42,7 @@ public class JwtAuthFilter extends OncePerRequestFilter {
                                     HttpServletResponse response,
                                     FilterChain filterChain) throws ServletException, IOException {
         String uri = request.getRequestURI();
+        String method = request.getMethod();
 
         // 公开路径始终放行
         if (isPublicPath(uri)) {
@@ -49,27 +54,32 @@ public class JwtAuthFilter extends OncePerRequestFilter {
 
         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;
             }
-        }
-
-        // 无 token
-        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);
     }
 
@@ -77,7 +87,6 @@ public class JwtAuthFilter extends OncePerRequestFilter {
         for (String prefix : PUBLIC_PREFIXES) {
             if (uri.startsWith(prefix)) return true;
         }
-        // 根路径 "/" 也公开
         return "/".equals(uri) || uri.endsWith(".html") || uri.endsWith(".js") || uri.endsWith(".css");
     }
 

+ 19 - 4
backend-java/src/main/java/com/pharmacopoeia/security/JwtUtil.java

@@ -3,6 +3,8 @@ package com.pharmacopoeia.security;
 import io.jsonwebtoken.*;
 import io.jsonwebtoken.security.Keys;
 import com.pharmacopoeia.config.JwtProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.crypto.SecretKey;
@@ -13,6 +15,8 @@ import java.util.Map;
 @Component
 public class JwtUtil {
 
+    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);
+
     private final JwtProperties jwtProperties;
     private final SecretKey key;
 
@@ -22,27 +26,38 @@ public class JwtUtil {
     }
 
     public String createToken(String subject) {
-        return Jwts.builder()
+        String token = Jwts.builder()
                 .subject(subject)
                 .issuedAt(new Date())
                 .expiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration()))
                 .signWith(key)
                 .compact();
+        log.info("JWT 创建成功: subject={}, expires={}", subject,
+                new Date(System.currentTimeMillis() + jwtProperties.getExpiration()));
+        return token;
     }
 
     public Claims parseToken(String token) {
-        return Jwts.parser()
+        Claims claims = Jwts.parser()
                 .verifyWith(key)
                 .build()
                 .parseSignedClaims(token)
                 .getPayload();
+        log.debug("JWT 解析成功: subject={}, issuedAt={}, expires={}",
+                claims.getSubject(), claims.getIssuedAt(), claims.getExpiration());
+        return claims;
     }
 
     public boolean validateToken(String token) {
         try {
-            parseToken(token);
+            Claims claims = parseToken(token);
+            log.debug("JWT 验证通过: subject={}", claims.getSubject());
             return true;
+        } catch (ExpiredJwtException e) {
+            log.warn("JWT 已过期: {}", e.getMessage());
+            return false;
         } catch (JwtException | IllegalArgumentException e) {
+            log.warn("JWT 验证失败: {}", e.getMessage());
             return false;
         }
     }
@@ -50,4 +65,4 @@ public class JwtUtil {
     public String getSubject(String token) {
         return parseToken(token).getSubject();
     }
-}
+}

+ 7 - 1
static/index.html

@@ -574,7 +574,13 @@ window.switchTab=function(tab){
   document.querySelectorAll('.nav-btn').forEach(function(b){b.classList.toggle('active',b.dataset.tab===tab)});
   document.getElementById('tabChat').classList.toggle('hidden',tab!=='chat');
   document.getElementById('tabDrugs').classList.toggle('hidden',tab!=='drugs');
-  if(tab==='drugs'){drugKeyword='';drugPage=1;document.getElementById('drugList').innerHTML='<div class="welcome"><div class="icon">⏳</div><p>加载中...</p></div>';document.getElementById('drugPager').innerHTML='';loadDrugList(1)}
+  if(tab==='drugs'){
+	    drugKeyword='';drugPage=1;
+	    showDrugList();
+	    document.getElementById('drugList').innerHTML='<div class="welcome"><div class="icon">⏳</div><p>加载中...</p></div>';
+	    document.getElementById('drugPager').innerHTML='';
+	    loadDrugList(1);
+	  }
 };
 // 初始化:获取 token → 打开本地数据库 → 恢复对话 → 预加载药品列表
 (async function init(){