WebConfig.java 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package com.etotem.cfc.config;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.core.Ordered;
  8. import org.springframework.core.annotation.Order;
  9. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  10. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  11. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  12. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  13. import javax.annotation.Resource;
  14. import javax.servlet.*;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.io.IOException;
  18. @Configuration
  19. public class WebConfig implements WebMvcConfigurer {
  20. private static final Logger log = LoggerFactory.getLogger(WebConfig.class);
  21. @Resource
  22. private JwtInterceptor jwtInterceptor;
  23. @Resource
  24. private FamilyAccessInterceptor familyAccessInterceptor;
  25. @Resource
  26. private OperationLogInterceptor operationLogInterceptor;
  27. @Resource
  28. private RateLimitInterceptor rateLimitInterceptor;
  29. @Value("${upload.base-dir:/data/cfc-uploads}")
  30. private String uploadBaseDir;
  31. @Value("${storage.type:local}")
  32. private String storageType;
  33. @Override
  34. public void addCorsMappings(CorsRegistry registry) {
  35. registry.addMapping("/**")
  36. .allowedOriginPatterns("https://cfc.etotem.com.cn", "http://cfc.etotem.com.cn", "https://www.etotem.com.cn", "http://www.etotem.com.cn", "https://cf-club.iwintrue.com", "http://cf-club.iwintrue.com", "https://cfc.iwintrue.com", "http://cfc.iwintrue.com", "https://www.cf-club.com", "http://www.cf-club.com", "https://cfc.cf-club.com", "http://cfc.cf-club.com", "https://111.228.6.214", "http://111.228.6.214", "http://localhost:*", "http://127.0.0.1:*")
  37. .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
  38. .allowedHeaders("*")
  39. .allowCredentials(true)
  40. .maxAge(3600);
  41. }
  42. /** Chrome Private Network Access: allow requests from non-secure contexts to private-network backend */
  43. @Bean
  44. @Order(Ordered.HIGHEST_PRECEDENCE)
  45. public Filter privateNetworkAccessFilter() {
  46. return new Filter() {
  47. @Override
  48. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  49. throws IOException, ServletException {
  50. HttpServletRequest req = (HttpServletRequest) request;
  51. HttpServletResponse resp = (HttpServletResponse) response;
  52. resp.setHeader("Access-Control-Allow-Private-Network", "true");
  53. chain.doFilter(request, response);
  54. }
  55. };
  56. }
  57. /** 诊断过滤器:记录所有请求的来源和状态码 */
  58. @Bean
  59. @Order(Ordered.HIGHEST_PRECEDENCE + 1)
  60. public Filter diagnosticFilter() {
  61. return new Filter() {
  62. @Override
  63. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  64. throws IOException, ServletException {
  65. HttpServletRequest req = (HttpServletRequest) request;
  66. long start = System.currentTimeMillis();
  67. chain.doFilter(request, response);
  68. int status = ((HttpServletResponse) response).getStatus();
  69. long ms = System.currentTimeMillis() - start;
  70. if (status == 403 || req.getRequestURI().contains("/api/payment")) {
  71. log.warn("[DIAG] {} {} -> {} ({}ms) origin={} ua={}",
  72. req.getMethod(), req.getRequestURI(), status, ms,
  73. req.getHeader("Origin"),
  74. req.getHeader("User-Agent"));
  75. }
  76. }
  77. };
  78. }
  79. @Override
  80. public void addInterceptors(InterceptorRegistry registry) {
  81. registry.addInterceptor(jwtInterceptor)
  82. .addPathPatterns("/api/**")
  83. .excludePathPatterns(
  84. "/api/auth/send-code",
  85. "/api/auth/phone-login",
  86. "/api/auth/wechat-login",
  87. "/api/auth/wechat-phone-login",
  88. "/api/auth/check-phone",
  89. "/api/auth/direct-register",
  90. "/api/auth/register-with-idcard",
  91. "/api/auth/wechat-web/qr",
  92. "/api/auth/wechat-web/poll",
  93. "/api/admin-auth/send-code",
  94. "/api/admin-auth/login",
  95. "/api/admin-auth/login-by-password",
  96. "/api/admin-auth/account-roles",
  97. "/api/product/list",
  98. "/api/product/detail",
  99. "/api/product/type-list",
  100. "/api/mini-game/list",
  101. "/api/articles/list",
  102. "/api/articles/detail",
  103. "/api/articles/categories",
  104. "/api/articles/featured",
  105. "/api/articles/record-read",
  106. "/api/growth/**",
  107. "/api/articles/updated-since",
  108. "/api/activity/list",
  109. "/api/activity/detail",
  110. "/api/ai/context",
  111. "/api/config/public/**",
  112. "/api/payment/wechat/native",
  113. "/api/payment/wechat/create",
  114. "/api/payment/wechat/status",
  115. "/api/payment/wechat/oauth2-url",
  116. "/api/payment/wechat/oauth2-callback",
  117. "/api/payment/wechat/v3-notify"
  118. );
  119. registry.addInterceptor(operationLogInterceptor)
  120. .addPathPatterns("/api/**");
  121. registry.addInterceptor(rateLimitInterceptor)
  122. .addPathPatterns("/api/**");
  123. registry.addInterceptor(familyAccessInterceptor)
  124. .addPathPatterns("/api/**")
  125. .excludePathPatterns(
  126. "/api/auth/send-code",
  127. "/api/auth/phone-login",
  128. "/api/auth/wechat-login",
  129. "/api/auth/wechat-phone-login",
  130. "/api/auth/check-phone",
  131. "/api/auth/direct-register",
  132. "/api/auth/register-with-idcard",
  133. "/api/auth/wechat-web/qr",
  134. "/api/auth/wechat-web/poll",
  135. "/api/admin-auth/send-code",
  136. "/api/admin-auth/login",
  137. "/api/admin-auth/login-by-password",
  138. "/api/admin-auth/account-roles",
  139. "/api/product/list",
  140. "/api/product/detail",
  141. "/api/product/type-list",
  142. "/api/mini-game/list",
  143. "/api/articles/list",
  144. "/api/articles/detail",
  145. "/api/articles/categories",
  146. "/api/articles/featured",
  147. "/api/articles/record-read",
  148. "/api/growth/**",
  149. "/api/activity/list",
  150. "/api/activity/detail",
  151. "/api/payment/wechat/v3-notify"
  152. );
  153. }
  154. @Override
  155. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  156. // 仅在本地存储模式下注册静态资源映射;jdcloud 模式文件走京东云公网URL
  157. if ("local".equals(storageType)) {
  158. registry.addResourceHandler("/uploads/**")
  159. .addResourceLocations("file:" + uploadBaseDir + "/");
  160. }
  161. }
  162. }