美文网首页
【问题排查】Spring Cloud Gateway 返回 40

【问题排查】Spring Cloud Gateway 返回 40

作者: 蓝笔头 | 来源:发表于2022-01-11 12:57 被阅读0次

    现象

    • GET 请求正常返回
    • POST 请求返回 403 Forbidden

    gateway access 日志:

    2022-01-11 11:56:52,488 : 10.0.16.132:15362 - - [11/Jan/2022:11:56:52 +0800] "GET /landing-page/experience-train-enroll/status?phoneNumber=15074470123 HTTP/1.1" 200 85 44 ms
    2022-01-11 11:56:52,589 : 10.0.16.132:15362 - - [11/Jan/2022:11:56:52 +0800] "POST /landing-page/verify/code HTTP/1.1" 403 0 12 ms
    

    问题分析

    请求路径为:【浏览器】==》【apache】==》【gateway】==》【后端 web 应用】

    注意:【apache】会将接收到的 HTTPS 请求,转换为 HTTP 请求转发到【gateway】

    跟踪 DefaultCorsProcessor 类的 process() 方法

    package org.springframework.web.cors.reactive;
    
    public class DefaultCorsProcessor implements CorsProcessor {
    ...
        public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
            ...
            // 1. 判断是否是跨域请求,如果不是直接返回 true
            if (!CorsUtils.isCorsRequest(request)) {
                return true;
            }
            ...
            return handleInternal(exchange, config, preFlightRequest);
        }
    
        protected boolean handleInternal(ServerWebExchange exchange,
                CorsConfiguration config, boolean preFlightRequest) {
            ...
            // 2. 获取请求头 Origin 的值
            String requestOrigin = request.getHeaders().getOrigin();
            String allowOrigin = checkOrigin(config, requestOrigin);
    
            // 4. 为 null 表示不满足条件,返回 403
            if (allowOrigin == null) {
                logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
                rejectRequest(response);
                return false;
            }
            ...
            return true;
        }
    
        // 3. 校验 requestOrigin 是否符合跨域的配置
        protected String checkOrigin(CorsConfiguration config, @Nullable String requestOrigin) {
            return config.checkOrigin(requestOrigin);
        }
    
        // 5. 设置 http 响应状态码为 403
        protected void rejectRequest(ServerHttpResponse response) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
        }
    ...
    }
    
    public abstract class CorsUtils {
        // 跨域请求需要满足下面两个条件
        // 1. 包含 Origin 请求头
        // 2. request 的 schema:host:port 和 origin 一致
        public static boolean isCorsRequest(ServerHttpRequest request) {
            return request.getHeaders().containsKey(HttpHeaders.ORIGIN) && !isSameOrigin(request);
        }
    }
    

    解决方案

    因为【apache】会把 HTTPS 协议请求转换为 HTTP 协议请求,而同源策略要求协议相同。

    所以转化后的请求不再是同源请求,因此需要在 application.yml 文件中新增如下配置。

    spring:
      cloud:
        gateway:
          globalcors:
            cors-configurations:
              '[/**]':
                allowedOrigins: ["https://xxx-gamma.wumii.net"] # 此行配置是重点
                allowedMethods:
                  - GET
                  - POST
                  - HEAD
                  - PUT
                  - DELETE
                allowedHeaders: '*'
                allowCredentials: true
            add-to-simple-url-handler-mapping: true
    

    以支持跨域请求。

    拓展阅读

    GET 请求为什么不会自动加上 Origin 请求头?

    因为:

    以下情况浏览器会带上 Origin
    1、所有跨域请求
    2、除了 GETHEAD 请求外的同源请求

    引用自 为什么有的请求,浏览器不会在请求头中自动加上 origin?

    参考

    相关文章

      网友评论

          本文标题:【问题排查】Spring Cloud Gateway 返回 40

          本文链接:https://www.haomeiwen.com/subject/ttpbcrtx.html