From 49c254ed5bdc8348551d808ee72579a6d2221e3b Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期六, 04 十月 2025 18:43:12 +0800
Subject: [PATCH] fix(miniprogram): 切换 GraphQL 端点为 /graphql,修复 review/index 400
---
backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 263 insertions(+), 5 deletions(-)
diff --git a/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java b/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java
index 32c4aa5..273489e 100644
--- a/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java
+++ b/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java
@@ -10,16 +10,22 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.ContentCachingRequestWrapper;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
/**
@@ -35,35 +41,279 @@
@Autowired
private UserRepository userRepository;
+
+ // 鍏佽鍖垮悕璁块棶鐨凣raphQL鏌ヨ鍒楄〃
+ private static final List<String> PUBLIC_GRAPHQL_QUERIES = Arrays.asList(
+ "carouselPlayList",
+ "activities",
+ "hello"
+ );
+
+ /**
+ * 鍒ゆ柇鏄惁搴旇璺宠繃JWT璁よ瘉
+ */
+ private boolean shouldSkipAuthentication(String requestURI) {
+ // 杩欎簺璺緞涓嶉渶瑕丣WT璁よ瘉锛堝凡鍘绘帀context path锛�
+ String[] skipPaths = {
+ "/auth/",
+ "/actuator/",
+ "/test/",
+ "/cleanup/",
+ "/upload/",
+ "/graphiql" // GraphiQL寮�鍙戝伐鍏�
+ // 娉ㄦ剰锛�/graphql 涓嶅湪璺宠繃鍒楄〃涓紝闇�瑕佺敱JWT杩囨护鍣ㄥ鐞嗕互鍖哄垎鍏紑鍜岀鏈夋煡璇�
+ };
+
+ for (String path : skipPaths) {
+ if (requestURI.startsWith(path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 妫�鏌ユ槸鍚︽槸GraphQL璇锋眰
+ */
+ private boolean isGraphQLRequest(String requestURI) {
+ return "/graphql".equals(requestURI);
+ }
+
+ /**
+ * 妫�鏌raphQL璇锋眰鏄惁涓哄叕寮�鏌ヨ锛堜笉闇�瑕佽璇侊級
+ * 鍙湁鏄庣‘鏍囪涓哄叕寮�鐨勬煡璇㈡墠鍏佽鍖垮悕璁块棶
+ */
+ private boolean isPublicGraphQLQuery(HttpServletRequest request) {
+ try {
+ // 妫�鏌ET鍙傛暟涓殑query
+ String query = request.getParameter("query");
+ if (query != null && !query.trim().isEmpty()) {
+ logger.debug("浠庡弬鏁拌幏鍙朑raphQL鏌ヨ: {}", query);
+ return containsPublicQuery(query);
+ }
+
+ // 瀵逛簬POST璇锋眰锛屽皾璇曡鍙栬姹備綋
+ if ("POST".equalsIgnoreCase(request.getMethod())) {
+ try {
+ // 浣跨敤CachedBodyHttpServletRequest鏉ヨ鍙栬姹備綋
+ String body = getRequestBody(request);
+ if (body != null && !body.trim().isEmpty()) {
+ logger.debug("浠庤姹備綋鑾峰彇GraphQL鏌ヨ: {}", body);
+
+ // 妫�鏌ontent-Type
+ String contentType = request.getContentType();
+ if (contentType != null && contentType.contains("application/graphql")) {
+ // 瀵逛簬application/graphql锛岃姹備綋鐩存帴鏄疓raphQL鏌ヨ
+ return containsPublicQuery(body);
+ } else {
+ // 瀵逛簬application/json锛岀畝鍗曡В鏋怞SON锛屾煡鎵緌uery瀛楁
+ if (body.contains("\"query\"")) {
+ return containsPublicQuery(body);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("璇诲彇POST璇锋眰浣撳け璐�", e);
+ }
+ }
+
+ return false;
+ } catch (Exception e) {
+ logger.error("瑙f瀽GraphQL璇锋眰澶辫触", e);
+ return false;
+ }
+ }
+
+ /**
+ * 璇诲彇璇锋眰浣撳唴瀹�
+ */
+ private String getRequestBody(HttpServletRequest request) {
+ try {
+ if (request instanceof ContentCachingRequestWrapper) {
+ ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
+ byte[] content = wrapper.getContentAsByteArray();
+ if (content.length > 0) {
+ return new String(content, wrapper.getCharacterEncoding());
+ }
+ }
+
+ // 濡傛灉涓嶆槸鍖呰鍣紝灏濊瘯鐩存帴璇诲彇锛堝彲鑳戒細娑堣�楄姹備綋锛�
+ StringBuilder buffer = new StringBuilder();
+ String line;
+ java.io.BufferedReader reader = request.getReader();
+ while ((line = reader.readLine()) != null) {
+ buffer.append(line);
+ }
+ return buffer.toString();
+ } catch (Exception e) {
+ logger.warn("璇诲彇璇锋眰浣撳け璐�", e);
+ return null;
+ }
+ }
+
+ /**
+ * 妫�鏌ユ煡璇㈠瓧绗︿覆鏄惁鍖呭惈鍏紑鏌ヨ
+ */
+ private boolean containsPublicQuery(String queryString) {
+ if (queryString == null || queryString.trim().isEmpty()) {
+ return false;
+ }
+
+ // 妫�鏌ユ槸鍚﹀寘鍚叕寮�鏌ヨ
+ for (String publicQuery : PUBLIC_GRAPHQL_QUERIES) {
+ if (queryString.contains(publicQuery)) {
+ logger.debug("妫�娴嬪埌鍏紑GraphQL鏌ヨ: {}", publicQuery);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 杩斿洖鏉冮檺閿欒鍝嶅簲
+ */
+ private void sendUnauthorizedResponse(HttpServletResponse response) throws IOException {
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ response.setContentType("application/json;charset=UTF-8");
+ response.getWriter().write("{\"errors\":[{\"message\":\"娌℃湁鏉冮檺璁块棶锛岃鍏堢櫥褰昞",\"extensions\":{\"code\":\"UNAUTHORIZED\"}}]}");
+ }
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
- FilterChain filterChain) throws ServletException, IOException {
+ FilterChain filterChain) throws ServletException, IOException {
+ String requestURI = request.getRequestURI();
+ String contextPath = request.getContextPath();
+
+ // 鍘绘帀context path锛屼笌Spring Security鐨勮涓轰繚鎸佷竴鑷�
+ String pathWithoutContext = requestURI;
+ if (contextPath != null && !contextPath.isEmpty() && requestURI.startsWith(contextPath)) {
+ pathWithoutContext = requestURI.substring(contextPath.length());
+ }
+
+ System.out.println("=== JWT杩囨护鍣ㄨ璋冪敤 === 鍘熷URI: " + requestURI + ", 鍘绘帀context path鍚�: " + pathWithoutContext);
+ logger.debug("JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: {}", pathWithoutContext);
+
+ // 璺宠繃涓嶉渶瑕佽璇佺殑璺緞
+ if (shouldSkipAuthentication(pathWithoutContext)) {
+ logger.debug("璺宠繃JWT璁よ瘉锛岃矾寰�: {}", pathWithoutContext);
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ // 瀵笹raphQL璇锋眰杩涜鐗规畩澶勭悊
+ if (isGraphQLRequest(pathWithoutContext)) {
+ logger.debug("妫�娴嬪埌GraphQL璇锋眰");
+
+ // 涓篜OST璇锋眰鍖呰璇锋眰浠ユ敮鎸侀噸澶嶈鍙栬姹備綋
+ HttpServletRequest wrappedRequest = request;
+ if ("POST".equalsIgnoreCase(request.getMethod())) {
+ wrappedRequest = new ContentCachingRequestWrapper(request);
+ }
+
+ // 鍏堟鏌uthorization澶达紝濡傛灉娌℃湁token锛屽啀妫�鏌ユ槸鍚︿负鍏紑鏌ヨ
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ logger.debug("GraphQL璇锋眰娌℃湁Authorization澶达紝妫�鏌ユ槸鍚︿负鍏紑鏌ヨ");
+
+ // 妫�鏌ユ槸鍚︿负鍏紑鏌ヨ
+ if (isPublicGraphQLQuery(wrappedRequest)) {
+ logger.debug("妫�娴嬪埌鍏紑GraphQL鏌ヨ锛屽厑璁稿尶鍚嶈闂�");
+
+ // 璁剧疆鍖垮悕璁よ瘉锛岃Spring Security鐭ラ亾杩欐槸涓�涓凡璁よ瘉鐨勫尶鍚嶇敤鎴�
+ AnonymousAuthenticationToken anonymousAuth = new AnonymousAuthenticationToken(
+ "anonymous",
+ "anonymous",
+ Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))
+ );
+ SecurityContextHolder.getContext().setAuthentication(anonymousAuth);
+ logger.debug("涓哄叕寮�GraphQL鏌ヨ璁剧疆鍖垮悕璁よ瘉");
+
+ filterChain.doFilter(wrappedRequest, response);
+ return;
+ }
+
+ logger.warn("GraphQL璇锋眰缂哄皯鏈夋晥鐨凙uthorization澶翠笖涓嶆槸鍏紑鏌ヨ");
+ sendUnauthorizedResponse(response);
+ return;
+ }
+
+ logger.debug("妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT");
+
+ String token = authHeader.substring(7);
+ try {
+ Long userId = jwtUtil.getUserIdFromToken(token);
+ if (userId == null || !jwtUtil.validateToken(token)) {
+ logger.warn("GraphQL璇锋眰鐨凧WT token鏃犳晥");
+ sendUnauthorizedResponse(response);
+ return;
+ }
+
+ // 鏌ユ壘鐢ㄦ埛淇℃伅骞惰缃璇�
+ Optional<User> userOpt = userRepository.findById(userId);
+ if (userOpt.isPresent()) {
+ User user = userOpt.get();
+ UsernamePasswordAuthenticationToken authToken =
+ new UsernamePasswordAuthenticationToken(
+ user.getId().toString(),
+ null,
+ new ArrayList<>()
+ );
+ authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+ logger.debug("GraphQL璇锋眰璁よ瘉鎴愬姛: userId={}", user.getId());
+ } else {
+ logger.warn("GraphQL璇锋眰鐨勭敤鎴蜂笉瀛樺湪: userId={}", userId);
+ sendUnauthorizedResponse(response);
+ return;
+ }
+ } catch (Exception e) {
+ logger.error("GraphQL璇锋眰JWT楠岃瘉澶辫触: {}", e.getMessage());
+ sendUnauthorizedResponse(response);
+ return;
+ }
+
+ // 缁х画澶勭悊璇锋眰
+ filterChain.doFilter(wrappedRequest, response);
+ return;
+ }
String authHeader = request.getHeader("Authorization");
String token = null;
Long userId = null;
+ logger.debug("Authorization澶�: {}", authHeader);
+
// 浠庤姹傚ご涓彁鍙朖WT token
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
+ logger.debug("鎻愬彇鍒癑WT token: {}", token.substring(0, Math.min(20, token.length())) + "...");
try {
userId = jwtUtil.getUserIdFromToken(token);
+ logger.debug("浠巘oken涓В鏋愬埌鐢ㄦ埛ID: {}", userId);
} catch (Exception e) {
- logger.debug("JWT token瑙f瀽澶辫触: {}", e.getMessage());
+ logger.error("JWT token瑙f瀽澶辫触: {}", e.getMessage(), e);
}
+ } else {
+ logger.debug("娌℃湁鎵惧埌Authorization澶存垨鏍煎紡涓嶆纭�");
}
- // 濡傛灉token鏈夋晥涓斿綋鍓嶆病鏈夎璇佷俊鎭�
- if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ // 濡傛灉token鏈夋晥涓斿綋鍓嶆槸鍖垮悕鎴栨棤璁よ瘉锛屽垯杩涜璁よ瘉
+ Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
+ boolean isAnonymous = (existingAuth == null) || ("anonymousUser".equals(String.valueOf(existingAuth.getPrincipal())));
+ if (userId != null && isAnonymous) {
+ logger.debug("寮�濮嬮獙璇乼oken鏈夋晥鎬�");
// 楠岃瘉token鏄惁鏈夋晥
if (jwtUtil.validateToken(token)) {
+ logger.debug("Token楠岃瘉鎴愬姛锛屾煡鎵剧敤鎴蜂俊鎭�");
// 鏌ユ壘鐢ㄦ埛淇℃伅
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isPresent()) {
User user = userOpt.get();
+ logger.debug("鎵惧埌鐢ㄦ埛: userId={}, phone={}", user.getId(), user.getPhone());
// 鍒涘缓璁よ瘉瀵硅薄
UsernamePasswordAuthenticationToken authToken =
@@ -76,9 +326,17 @@
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
- logger.debug("鐢ㄦ埛璁よ瘉鎴愬姛: userId={}, phone={}", user.getId(), user.getPhone());
+ logger.info("鐢ㄦ埛璁よ瘉鎴愬姛: userId={}, phone={}", user.getId(), user.getPhone());
+ } else {
+ logger.warn("鐢ㄦ埛涓嶅瓨鍦�: userId={}", userId);
}
+ } else {
+ logger.warn("Token楠岃瘉澶辫触");
}
+ } else if (userId == null) {
+ logger.debug("娌℃湁瑙f瀽鍒扮敤鎴稩D");
+ } else {
+ logger.debug("宸插瓨鍦ㄩ潪鍖垮悕璁よ瘉淇℃伅锛岃烦杩嘕WT璁よ瘉");
}
filterChain.doFilter(request, response);
--
Gitblit v1.8.0