From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-gateway/src/main/java/jnpf/filter/MvcSecurityConfig.java |  149 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 149 insertions(+), 0 deletions(-)

diff --git a/jnpf-gateway/src/main/java/jnpf/filter/MvcSecurityConfig.java b/jnpf-gateway/src/main/java/jnpf/filter/MvcSecurityConfig.java
new file mode 100644
index 0000000..9933cf2
--- /dev/null
+++ b/jnpf-gateway/src/main/java/jnpf/filter/MvcSecurityConfig.java
@@ -0,0 +1,149 @@
+package jnpf.filter;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaRequest;
+import cn.dev33.satoken.context.model.SaResponse;
+import cn.dev33.satoken.filter.SaFilterAuthStrategy;
+import cn.dev33.satoken.router.SaRouter;
+import jnpf.constant.GlobalConst;
+import jnpf.consts.AuthConsts;
+import jnpf.properties.MvcSecurityProperties;
+import jnpf.properties.SecurityProperties;
+import jnpf.util.Constants;
+import jnpf.util.ReactorUtil;
+import jnpf.util.UserProvider;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsConfigurationSource;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+/**
+ * mvc閰嶇疆
+ *
+ * @author JNPF寮�鍙戝钩鍙扮粍
+ * @version V5.0.0
+ * @copyright 寮曡繄淇℃伅鎶�鏈湁闄愬叕鍙革紙https://www.jnpfsoft.com锛�
+ * @date 2025-01-21
+ */
+@Slf4j
+@Configuration(proxyBeanMethods = false)
+public class MvcSecurityConfig {
+
+    private static final String DOMAIN_FORMAT = "%s://%s";
+    private static final String HOST_FORMAT = "%s:%s";
+
+    @Autowired
+    private MvcSecurityProperties mvcSecurityProperties;
+
+    @Autowired
+    private SecurityProperties securityProperties;
+
+    /**
+     * Header杩囨护鍣紝 涓鸿姹傛坊鍔� Id-Token
+     * @return
+     */
+    @Bean
+    public HttpHeadersFilter getMyHeadersFilter(){
+        return (input, exchange) -> {
+            if(securityProperties.isEnableInnerAuth() || securityProperties.isEnablePreAuth()){
+                input.add(AuthConsts.INNER_GATEWAY_TOKEN_KEY, UserProvider.getInnerAuthToken());
+            }
+            input.remove(GlobalConst.HEADER_HOST);
+            InetSocketAddress host = input.getHost();
+            if(host != null){
+                input.add(GlobalConst.HEADER_HOST, String.format(HOST_FORMAT, host.getHostString(), host.getPort()));
+            }
+
+            return input;
+        };
+    }
+
+    @Bean
+    public GlobalFilter getContextGatewayFilterForReactor(){
+        return new ContextGatewayFilterForReactor();
+    }
+
+
+    @Bean
+    public CorsWebFilter corsFilter() {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        CorsConfiguration config = new CorsConfiguration();
+        // 鍏佽鍙戦�佸嚟鎹�
+        config.setAllowCredentials(true);
+        //鍏佽浠绘剰鍩熷悕璺ㄥ煙璁块棶鎺ュ彛
+        config.setAllowedOrigins(mvcSecurityProperties.getCors().getAllowedOrigins());
+        config.setAllowedOriginPatterns(mvcSecurityProperties.getCors().getAllowedOriginPatterns());
+        // 鍏佽鎵�鏈夊ご閮ㄤ俊鎭�
+        config.setAllowedHeaders(mvcSecurityProperties.getCors().getAllowedHeaders());
+        // 鍏佽鎵�鏈夎姹傛柟娉�
+        config.setAllowedMethods(mvcSecurityProperties.getCors().getAllowedMethods());
+        // 搴旂敤浜庢墍鏈夎矾寰�
+        source.registerCorsConfiguration("/**", config);
+        return new MyCorsFilter(source);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public SaFilterAuthStrategy defaultBeforeAuthStrategy() {
+        CorsConfiguration csrfConfiguration;
+        if(!mvcSecurityProperties.getCsrfOrigins().isEmpty() || !mvcSecurityProperties.getCsrfOriginsPatterns().isEmpty()) {
+            csrfConfiguration = new CorsConfiguration();
+            csrfConfiguration.setAllowedOrigins(mvcSecurityProperties.getCsrfOrigins());
+            csrfConfiguration.setAllowedOriginPatterns(mvcSecurityProperties.getCsrfOriginsPatterns());
+        } else {
+            csrfConfiguration = null;
+        }
+        return obj -> {
+            SaRequest request = SaHolder.getRequest();
+            // ---------- 璁剧疆璺ㄥ煙鍝嶅簲澶� ----------
+            SaResponse response = SaHolder.getResponse();
+            if(!ObjectUtils.isEmpty(mvcSecurityProperties.getHeaders().getServerName())){
+                response.setServer(mvcSecurityProperties.getHeaders().getServerName());
+            }
+            if(!ObjectUtils.isEmpty(mvcSecurityProperties.getHeaders().getXFrameOptions()) && !MvcSecurityProperties.XFrameOptionsMode.DISABLED.equals(mvcSecurityProperties.getHeaders().getXFrameOptions())){
+                response.setHeader(MvcSecurityProperties.HEADER_XFRAME_OPTIONS, mvcSecurityProperties.getHeaders().getXFrameOptions().getMode());
+            }
+            if(!ObjectUtils.isEmpty(mvcSecurityProperties.getHeaders().getXXssProtection()) && !MvcSecurityProperties.XXssProtectionMode.DISABLED.equals(mvcSecurityProperties.getHeaders().getXXssProtection())){
+                response.setHeader(MvcSecurityProperties.HEADER_XSS_PROTECTION, mvcSecurityProperties.getHeaders().getXXssProtection().getMode());
+            }
+            if(!ObjectUtils.isEmpty(mvcSecurityProperties.getHeaders().getXContentTypeOptions()) && !MvcSecurityProperties.XContentTypeOptions.DISABLED.equals(mvcSecurityProperties.getHeaders().getXContentTypeOptions())){
+                response.setHeader(MvcSecurityProperties.HEADER_CONTENT_TYPE_OPTIONS, mvcSecurityProperties.getHeaders().getXContentTypeOptions().getMode());
+            }
+
+            if(csrfConfiguration != null){
+                String referer = request.getHeader("referer");
+                if(!ObjectUtils.isEmpty(referer)) {
+                    URI uri = URI.create(referer);
+                    String refererDomain = String.format(DOMAIN_FORMAT, uri.getScheme(), uri.getAuthority());
+                    String allowOrign = csrfConfiguration.checkOrigin(refererDomain);
+                    if(ObjectUtils.isEmpty(allowOrign)){
+                        log.error("Reject CSRF Request: {}, {}, {}, {}", request.getRequestPath(), referer, ReactorUtil.getIpAddr(request), request.getHeader(Constants.AUTHORIZATION));
+                        response.setStatus(HttpStatus.FORBIDDEN.value());
+                        SaRouter.back("Invalid CSRF Request");
+                    }
+                }
+            }
+        };
+    }
+
+
+    @Order(-110)
+    public static class MyCorsFilter extends CorsWebFilter {
+        public MyCorsFilter(CorsConfigurationSource configSource) {
+            super(configSource);
+        }
+    }
+}

--
Gitblit v1.8.0