个人踩坑之旅-跨域问题引起bug

场景:前端

        因为业务缘由须要在请求中添加一个信息代表请求的source,通过一轮方案的评审,你们共同决定把这source信息存放在消息header中。前端小伙伴听完以后内心暗自偷笑:就一行的代码的事,请求的时候在消息头中添加source:xxxx,这么轻松。而后也没有测试就直接发布了。刚发布没多久,用户纷纷过来投诉各类页面打不开。跨域

cause:浏览器

       用户刚投诉,小伙伴们纷纷跑到后台抓取日志查看,可是啥也没有。只能亲测了页面了,一打开页面立马在console上看到各类红点,全显示各类跨域问题。当时就纳闷了,项目中有考虑跨域问题呀,后台配置了CORSFilter,之前一直也没有什么问题呀。通过各类确认以后才发现是因为前端加了自定义消息头的缘由。安全

跨域流程具体分析:服务器

      说到跨域,首先有已个关键词:同源性。同源性指的是当前页面的协议号,域名,端口号与所请求的资源的协议号,域名,端口号保持一致。浏览器为何要这么作呢?这是从安全方面考虑,防止XSS攻击。可是现实场景中,不少状况又不得不跨域请求。这该怎么办呢?W3C增长了CORS标准,这个标准容许浏览器向跨源服务器发出XMLHttpRequest请求。CORS须要浏览器和服务器同时支持。在跨域请求中能够分为简单请求和非简单请求,简单请求指的是——请求方法为GET,POST,HEAD,请求响应头只能为:Accept,Accept-Language,Last-Event-ID,Content-type,且Content-type的值只能为application/x-www-form-urlencoded、multipart/form-data、text/plain。其它的请求都属于非简单请求。cookie

      简单请求:简单请求相对于非跨域请求在请求header中添加了一个origin头,代表本次请求的来源,服务器收到请求,根据origin头判断是否支持本次请求,若是支持返回响应结果,并在响应中添加了一个Access-Control-Allow-Origin头,若是服务器不支持本次请求,会返回一个正常的http响应,正常的响应指的是http status code正常,可是没有响应内容。app

      非简单请求:非简单请求相对于非跨域请求增长了一次请求,此次请求俗称“预检”请求,这个请求的request method 是OPTIONS方法,header头中有origin和Access-Control-Request-Header,origin头的意义跟上文的一致,Access-Control-Request-Header头包含的信息是本次请求(真实请求,不是指嗅探请求)的全部请求头。这个请求顾名思义就是嗅探下服务器是否支持本次请求。嗅探的内容包括:服务器是否支持真实请求的request method,是否支持真实请求的消息头(具体是从Access-Control-Request-Header取出值,而后跟服务器配置的可接受消息头进行比对),查看当前网页所在的域名是否在服务器的许可名单中(查看origin头中的值是否是在服务器的容许域名列表中),若服务器不支持本次真实请求的话,嗅探请求会返回403,真实请求也不会发生了。cors

    服务器端的配置:以上讨论的是浏览器端作的操做,固然那些操做用户没法感知,程序猿也不须要特别开发,一切都是浏览器自主完成,目前大部分浏览器都支持CORS。CORS须要浏览器和服务器共同配合完成,那服务器端须要配置什么呢?总结起来就那么几点:哪些域名是服务器承认得,哪些请求方法是服务器承认的,哪些请求头是服务承认的,是否容许设置cookie。下面利用spingMVC的CORSFilter来配置服务器(不止这一种方案,还有其余诸如在Nginx配置响应头)做个示例,具体以下:测试

<filter>
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
        <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>*</param-value>
        </init-param>
        <init-param>
//配置支持的方法
            <param-name>cors.supportedMethods</param-name>
            <param-value>GET,POST, HEAD, PUT, DELETE</param-value>
        </init-param>
        <init-param>
//配置支持的消息头
            <param-name>cors.supportedHeaders</param-name>
            <param-value>Accept,Origin, Authorization, X-Requested-With, Content-Type, Last-Modified</param-value>
        </init-param>
        <init-param>
//配置响应结果的暴露的消息头
            <param-name>cors.exposedHeaders</param-name>
            <param-value>Set-Cookie</param-value>
        </init-param>
        <init-param>
//是否容许cookie
            <param-name>cors.supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CORS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

服务器端配置以后,就能够进行“预检”请求了,“预检“”请求以后,你会发现响应消息头中相对于非跨域请求会增长几个消息头:Access-Contol-Allow-Origin(代表服务器容许的消息来源域),Access-Control-Allow-Method(代表服务器容许的请求方法),Access-Control-Allow-Header(代表服务器容许的请求头),Access-Control-Allow-Credentials(是否容许Cookie,若不容许,前端就拿不到cookie了),浏览器收到这几个头以后,以为真实请求知足这些条件,接下来就会发起真实的请求了。url

总结:回到最开始的bug问题,这个问题是由于前端开发人员在请求中自定义了一个消息头source,不在简单请求的范围内,属于非简单请求,在发起“预检”请求时,因为后台CORSFilter中没有配置容许该请求头,倒致“预检”请求403。

相关文章
相关标签/搜索