動態

詳情 返回 返回

微服務跨域問題|The 'Access-Control-Allow-Origin' header contains - 動態 詳情

個人博客:無奈何楊(wnhyang)

個人語雀:wnhyang

共享語雀:在線知識共享

Github:wnhyang - Overview


參考

Spring Cloud Gateway CORS 方案看這篇就夠了-騰訊雲開發者社區-騰訊雲

九種跨域方式實現原理(完整版) - 掘金

什麼是跨域,有哪些解決方法

以下為AI回答

跨域是指由於瀏覽器的安全機制,當一個頁面嘗試通過 XMLHttpRequest 或 Fetch API 等方式向不同於當前頁面源(協議、域名、端口三者之一不同的 URL)發起請求時,會被瀏覽器阻止,這稱為“同源策略”限制。這是為了防止惡意網站通過腳本對其他網站的數據進行非法訪問。

解決跨域問題的方法有很多種,以下是常見的幾種:

1、CORS(Cross-Origin Resource Sharing)

CORS 是一種 W3C 標準,允許服務器表明哪些其他源可以請求資源。服務器通過設置響應頭(如 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers 等)來允許特定源或者所有源進行跨域請求。客户端(瀏覽器)在接收到響應後判斷是否符合 CORS 規則,若符合則允許數據交互。

2、JSONP(JSON with Padding)

JSONP 是一種非正式傳輸協議,它利用 <script> 標籤不受同源策略限制的特點,通過回調函數的形式來加載跨域數據。JSONP 只支持 GET 請求,並且要求服務器端配合提供 JSONP 形式的響應。

3、代理服務器(Proxy Server)

通過在服務器端設置一個代理,使得所有的請求都先經過這個代理服務器,然後由代理服務器轉發給目標服務器,這樣在瀏覽器看來,始終是在同一個源下進行通信。

4、document.domain + iframe

對於兩個同頂級域名下的頁面,可以通過修改各自的 document.domain 屬性來允許它們之間的通信。

5、window.postMessage()

HTML5 提供了 window.postMessage 方法,允許來自不同源的腳本採用異步方式進行有限的通信,可以在窗口間發送消息。

6、WebSockets

雖然 WebSocket 連接建立之初也會遇到同源策略限制,但它本身提供了機制來處理跨域連接,服務器可以在握手階段設置 Origin 頭部來控制哪些源可以建立連接。

現代 web 開發中,CORS 是最常用的跨域解決方案,因為它支持更廣泛的請求類型(不僅僅是 GET)和更安全的通信模式。然而,在某些特定場景下,如兼容舊版瀏覽器或者簡單的 GET 請求,JSONP 仍然是一個可行的選擇。

Spring Cloud Gateway微服務跨域問題

當我們在網關和子服務都配置了CORS允許特定源的跨域請求時,還是會出現以下問題。

The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:5173', but only one is allowed.

從提示來看,是Access-Control-Allow-Origin頭包含了多個值。

果然,Access-Control-Allow-Origin確實包含了多個值,這就是問題根源,具體的分析可以參考Spring Cloud Gateway CORS 方案看這篇就夠了-騰訊雲開發者社區-騰訊雲文章。

解決方案

既然是重複配置了,那麼去掉一個就行了,如下是我的子服務和網關的配置,我是刪去了子服務的配置。

當然還有一種方法是在response中去除重複header的。

子服務跨域配置

/**
 * @author wnhyang
 * @date 2024/3/14
 **/
@Configuration
@Slf4j
public class WebConfig {

    /**
     * 跨域配置
     */
    @Bean
    public CorsFilter corsFilter() {
        log.info("[CorsFilter][初始化corsFilter配置]");
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 設置訪問源地址
        config.addAllowedOriginPattern("*");
        // 設置訪問源請求頭
        config.addAllowedHeader("*");
        // 設置訪問源請求方法
        config.addAllowedMethod("*");
        // 有效期 1800秒
        config.setMaxAge(1800L);
        // 添加映射路徑,攔截一切請求
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        // 返回新的CorsFilter
        return new CorsFilter(source);
    }
}

網關跨域配置

/**
 * 跨域配置
 *
 * @author wnhyang
 * @date 2023/8/25
 */
@Component
public class GlobalCorsFilter implements WebFilter, Ordered {

    /**
     * 這裏為支持的請求頭,如果有自定義的header字段請自己添加
     */
    private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Language, Content-Type, Authorization, credential, X-XSRF-TOKEN, isToken, token, Admin-Token, App-Token";
    private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
    private static final String ALLOWED_ORIGIN = "*";
    private static final String ALLOWED_EXPOSE = "*";
    private static final String MAX_AGE = "18000L";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (CorsUtils.isCorsRequest(request)) {
            ServerHttpResponse response = exchange.getResponse();
            HttpHeaders headers = response.getHeaders();
            headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
            headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
            headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
            headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
            headers.add("Access-Control-Max-Age", MAX_AGE);
            headers.add("Access-Control-Allow-Credentials", "true");
            if (request.getMethod() == HttpMethod.OPTIONS) {
                response.setStatusCode(HttpStatus.OK);
                return Mono.empty();
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

寫在最後

拙作艱辛,字句心血,望諸君垂青,多予支持,不勝感激。


個人博客:無奈何楊(wnhyang)

個人語雀:wnhyang

共享語雀:在線知識共享

Github:wnhyang - Overview

Add a new 評論

Some HTML is okay.