美文网首页
因为HTTP版本的差异导致跨域配置失效

因为HTTP版本的差异导致跨域配置失效

作者: 小驴小驴 | 来源:发表于2022-11-21 00:09 被阅读0次

    背景:后端服务中明明已经配置了跨域,但是一直未生效,最后发现是因为不同的HTTP版本对Header头的要求不同导致的。

    一、背景:

    我们有个需求:将APP端的所有功能复制一份到PC端。为了区分流量到底是来自APP还是PC。

    因此让前端小伙伴在HTTP Header头加上 clientSource 字段,来自APP端的流量则clientSource=app,否则clientSource=pc。

    然后后端利用AOP技术 编写切面(包含切点、通知)。

    在切面中打印日志的地方加上clientSource的取值,这样当某个流量触发后端导致异常时,我们可以根据LOG日志快速定位出流量来源。

    因此,第一版Cors如下配置:

    @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest servletRequest = (HttpServletRequest) request;
            HttpServletResponse servletResponse = (HttpServletResponse) response;
    
            /**
             * Origin 头设置
             */
            String origin = servletRequest.getHeader("Origin");
            if (StringUtils.isBlank(origin)) {
                servletResponse.setHeader("Access-Control-Allow-Origin", "*");
            } else {
                servletResponse.setHeader("Access-Control-Allow-Origin", origin);
            }
    
            /**
             * Header 头设置
             */
            StringBuffer allowHeaders = new StringBuffer("clientSource,");
            Enumeration<String> headerNames = servletRequest.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                allowHeaders.append(headerNames.nextElement()).append(",");
            }
            servletResponse.setHeader("Access-Control-Allow-Headers", allowHeaders.deleteCharAt(allowHeaders.length() - 1).toString());
    
            /**
             * Methods 头设置
             */
            servletResponse.setHeader("Access-Control-Allow-Methods", "GTP,POST,PUT,DELETE,OPTIONS");
    
            /**
             * 开启Cookie每次传输
             */
            servletResponse.setHeader("Access-Control-Allow-Credentials", "true");
    
            if (OPTIONS.equalsIgnoreCase(servletRequest.getMethod())) {
                return;
            }
    
            chain.doFilter(request, response);
        }
    
    

    现象:前端按约定添加了clientSourceheader,但是浏览器Network现实,每一次的OPTIONS(所有带有自定义Header与非GET请求 两者满足其一都会发送OPTIONS预发请求)都无法通过,导致跨域了。

    二、解决办法

    摘自HTTP2规范:

    具体见:RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)

    8.1.2\.  HTTP Header Fields
    
       HTTP header fields carry information as a series of key-value pairs.
       For a listing of registered HTTP headers, see the "Message Header
       Field" registry maintained at <https://www.iana.org/assignments/
       message-headers>.
    
       Just as in HTTP/1.x, header field names are strings of ASCII
       characters that are compared in a case-insensitive fashion.  However,
       header field names MUST be converted to lowercase prior to their
       encoding in HTTP/2\.  A request or response containing uppercase
       header field names MUST be treated as malformed (Section 8.1.2.6).
    
    

    上面的意思大致是:

    与HTTP1.x相同,Header是ASCII码,但是HTTP2的Header Name必须是全小写的。因此将上面的文章修改成如下即可:

    /**
     * Header 头设置
     */
     StringBuffer allowHeaders = new StringBuffer("clientSource,clientsource");
     Enumeration<String> headerNames = servletRequest.getHeaderNames();
     while (headerNames.hasMoreElements()) {
             allowHeaders.append(headerNames.nextElement()).append(",");
     }
     servletResponse.setHeader("Access-Control-Allow-Headers", allowHeaders.deleteCharAt(allowHeaders.length() - 1).toString());
    
    

    三、疑问

    你可能会有将Access-Control-Allow-Headers设置为“*”,这确实是一个不错的办法,可以非常方便的解决了HTTP不同版本Header的兼容性问题。

    但是这里设置的servletResponse.setHeader("Access-Control-Allow-Credentials", "true");导致所有的配置无法使用“”,同样,其他的几个配置也都无法使用“”;

    具体见:

    DevDocs-access-control-allow-origin的说明

    对于Access-Control-Allow-Credentials请移步这:

    DevDocs-Access-Control-Allow-Credentials的说明

    相关文章

      网友评论

          本文标题:因为HTTP版本的差异导致跨域配置失效

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