From afeeed281e60466b576fbe74d339634cc5d07b82 Mon Sep 17 00:00:00 2001 From: Codex Assistant <codex@example.com> Date: 星期三, 08 十月 2025 08:56:42 +0800 Subject: [PATCH] 修复评审功能和用户认证问题 --- backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java | 251 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 236 insertions(+), 15 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 96e6413..328fe4f 100644 --- a/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java @@ -10,7 +10,9 @@ 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; @@ -18,9 +20,12 @@ 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; /** @@ -37,6 +42,13 @@ @Autowired private UserRepository userRepository; + // 鍏佽鍖垮悕璁块棶鐨凣raphQL鏌ヨ鍒楄〃 + private static final List<String> PUBLIC_GRAPHQL_QUERIES = Arrays.asList( + "carouselPlayList", + "activities", + "hello" + ); + /** * 鍒ゆ柇鏄惁搴旇璺宠繃JWT璁よ瘉 */ @@ -48,8 +60,8 @@ "/test/", "/cleanup/", "/upload/", - "/graphql", - "/graphiql" + "/graphiql" // GraphiQL寮�鍙戝伐鍏� + // 娉ㄦ剰锛�/graphql 涓嶅湪璺宠繃鍒楄〃涓紝闇�瑕佺敱JWT杩囨护鍣ㄥ鐞嗕互鍖哄垎鍏紑鍜岀鏈夋煡璇� }; for (String path : skipPaths) { @@ -59,6 +71,107 @@ } 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) { + String encoding = wrapper.getCharacterEncoding() != null ? wrapper.getCharacterEncoding() : "UTF-8"; + return new String(content, encoding); + } + } + // 涓嶄粠鍘熷璇锋眰娴佽鍙栵紝閬垮厤涓嬫父缁勪欢鎷夸笉鍒拌姹備綋瀵艰嚧 400 + return null; + } 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 @@ -80,6 +193,100 @@ 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鏌ヨ锛屽厑璁稿尶鍚嶈闂�"); + AnonymousAuthenticationToken anonymousAuth = new AnonymousAuthenticationToken( + "anonymous", + "anonymous", + Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")) + ); + SecurityContextHolder.getContext().setAuthentication(anonymousAuth); + filterChain.doFilter(wrappedRequest, response); + return; + } + + // 鏃犳硶鍙潬璇诲彇/鍒ゅ畾璇锋眰浣撴椂锛岄粯璁や互鍖垮悕韬唤鏀捐鍒癎raphQL灞傦紝鐢卞悇Resolver鑷杩涜鏉冮檺鏍¢獙 + logger.debug("鏃犳硶鍙潬鍒ゅ畾鏄惁涓哄叕寮�鏌ヨ锛岃缃尶鍚嶈璇佸苟浜ょ敱GraphQL灞傚鐞�"); + AnonymousAuthenticationToken anonymousAuth = new AnonymousAuthenticationToken( + "anonymous", + "anonymous", + Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")) + ); + SecurityContextHolder.getContext().setAuthentication(anonymousAuth); + filterChain.doFilter(wrappedRequest, 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; + } + + // 妫�鏌ユ槸鍚︿负鍖垮悕鐢ㄦ埛锛堣礋鏁扮敤鎴稩D锛� + if (userId < 0) { + // 鍖垮悕鐢ㄦ埛锛岃缃壒娈婄殑璁よ瘉淇℃伅 + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken( + "anonymous_" + userId, + null, + Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")) + ); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + logger.debug("GraphQL璇锋眰鍖垮悕鐢ㄦ埛璁よ瘉鎴愬姛: userId={}", userId); + } else { + // 姝e父鐢ㄦ埛锛屾煡鎵剧敤鎴蜂俊鎭苟璁剧疆璁よ瘉 + 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; } @@ -113,26 +320,40 @@ 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()); - - // 鍒涘缓璁よ瘉瀵硅薄 + // 妫�鏌ユ槸鍚︿负鍖垮悕鐢ㄦ埛锛堣礋鏁扮敤鎴稩D锛� + if (userId < 0) { + // 鍖垮悕鐢ㄦ埛锛岃缃壒娈婄殑璁よ瘉淇℃伅 UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - user.getId().toString(), + "anonymous_" + userId, null, - new ArrayList<>() // 鏆傛椂涓嶈缃潈闄愶紝鍚庣画鍙互鏍规嵁瑙掕壊璁剧疆 + Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")) ); - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); - - logger.info("鐢ㄦ埛璁よ瘉鎴愬姛: userId={}, phone={}", user.getId(), user.getPhone()); + logger.info("鍖垮悕鐢ㄦ埛璁よ瘉鎴愬姛: userId={}", userId); } else { - logger.warn("鐢ㄦ埛涓嶅瓨鍦�: userId={}", userId); + // 姝e父鐢ㄦ埛锛屾煡鎵剧敤鎴蜂俊鎭� + Optional<User> userOpt = userRepository.findById(userId); + if (userOpt.isPresent()) { + User user = userOpt.get(); + logger.debug("鎵惧埌鐢ㄦ埛: userId={}, phone={}", user.getId(), user.getPhone()); + + // 鍒涘缓璁よ瘉瀵硅薄 + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken( + user.getId().toString(), + null, + new ArrayList<>() // 鏆傛椂涓嶈缃潈闄愶紝鍚庣画鍙互鏍规嵁瑙掕壊璁剧疆 + ); + + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + + logger.info("鐢ㄦ埛璁よ瘉鎴愬姛: userId={}, phone={}", user.getId(), user.getPhone()); + } else { + logger.warn("鐢ㄦ埛涓嶅瓨鍦�: userId={}", userId); + } } } else { logger.warn("Token楠岃瘉澶辫触"); -- Gitblit v1.8.0