zoukankan      html  css  js  c++  java
  • week 6 CORS

    1. CORS 简介

    同源策略( same origin policy )是浏览器安全的基石。在同源策略的限制下,非同源的网站之间不能发送 ajax 请求的。

    为了解决这个问题, w3c 提出了跨源资源共享,即 CORS(Cross-Origin Resource Sharing)。

    CORS 做到了两点:

    1. 不破坏即有规则
    2. 服务器实现了 CORS 接口,就可以跨源通信

    基于这两点, CORS 将请求分为两类:简单请求和非简单请求。

    1.1 简单请求

    可以先看下 CORS 出现前的情况:跨源时能够通过 script 或者 image 标签触发 GET 请求或通过表单发送一条 POST 请求,但这两种请求 HTTP 头信息中都不能包含任何自定义字段。

    简单请求对应该规则,因此对简单请求的定义为:

    请求方法是 HEADGET 或 POST 且 HTTP 头信息不超过以下几个字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type(只限于 application/x-www-form-urlencodedmultipart/form-datatext/plain)。

    比如有一个简单请求:

    GET /test HTTP/1.1
    Accept: */*
    Accept-Encoding: gzip, deflate, sdch, br
    Origin: http://www.examples.com
    Host: www.examples.com
    

    对于这样的简单请求, CORS 的策略是请求时,在头信息中添加一个 Origin 字段,服务器收到请求后,根据该字段判断是否允许该请求。

    1. 如果允许,则在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段,并返回正确的结果
    2. 如果不允许,则不在头信息中添加 Access-Control-Allow-Origin 字段。

    浏览器先于用户得到返回结果,根据有无 Access-Control-Allow-Origin 字段来决定是否拦截该返回结果。

    对于 CORS 出现前的一些服务, CORS 对他们的影响分两种情况:

    1. script 或者 image 触发的 GET 请求不包含 Origin 头,所以不受到 CORS 的限制,依旧可用。
    2. 如果是 ajax 请求, HTTP 头信息中会包含 Origin 字段,由于服务器没有做任何配置,所以返回结果不会包含 Access-Control-Allow-Origin,因此返回结果会被浏览器拦截,接口依旧不可以被 ajax 跨源访问。

    可以看出, CORS 的出现,没有对”旧的“服务造成任何影响。

    另外,除了提到的 Access-Control-Allow-Origin 还有几个字段用于描述 CORS 返回结果:

    1. Access-Control-Allow-Credentials: 可选,用户是否可以发送、处理 cookie 。
    2. Access-Control-Expose-Headers :可选,可以让用户拿到的字段。有几个字段无论设置与否都可以拿到的,包括: Cache-Control 、 Content-Language 、 Content-Type 、 Expires 、 Last-Modified 、 Pragma 。

    1.2 非简单请求

    除了简单请求之外的请求,就是非简单请求。

    对于非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次 OPTION 请求,称为预检请求( preflight request )。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。

    比如对于 DELETE 请求:

    OPTIONS /test HTTP/1.1
    Origin: http://www.examples.com
    Access-Control-Request-Method: DELETE
    Access-Control-Request-Headers: X-Custom-Header
    Host: www.examples.com
    

    与 CORS 相关的字段有:

    1. Access-Control-Request-Method: 真实请求使用的 HTTP 方法。
    2. Access-Control-Request-Headers: 真实请求中包含的自定义头字段。

    服务器收到请求时,需要分别对 Origin 、 Access-Control-Request-Method 、 Access-Control-Request-Headers 进行验证,验证通过后,会在返回 Http 头信息中添加

    Access-Control-Allow-Origin: http://www.examples.com
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: X-Custom-Header
    Access-Control-Allow-Credentials: true
    Access-Control-Max-Age: 1728000
    

    他们的含义分别是:

    1. Access-Control-Allow-Methods: 真实请求允许的方法
    2. Access-Control-Allow-Headers: 服务器允许使用的字段
    3. Access-Control-Allow-Credentials: 是否允许用户发送、处理 cookie
    4. Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求

    当预检请求通过后,浏览器会发送真实请求到服务器。这就实现了跨源请求

    服务端配置CORS

    1. 通过WebMvcConfigurerAdapter#addCorsMappings去配置

      public class WebConfig extends WebMvcConfigurerAdapter {
           @Override
           public void addCorsMappings(CorsRegistry registry) {
               registry.addMapping("/**")
                       .allowedHeaders("*")
                       .allowedMethods("*")
                       .allowedOrigins("*");
           }
       }
    2. 通过自定义Filter

       @Bean
       public FilterRegistrationBean corsFilter() {
           UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
           CorsConfiguration config = new CorsConfiguration();
           config.addAllowedOrigin("*");
           config.setAllowCredentials(true);
           config.addAllowedHeader("*");
           config.addAllowedMethod("*");
           source.registerCorsConfiguration("/**", config);
      
           FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
           bean.setOrder(0);
           return bean;
       }

    这两种方式实现的机制完全不一样,也就是产生作用的生命周期不一样。

    • 方式一,WebMvcConfigurerAdapter的配置最终将会转换为RequestMappingHandlerMapping,而这个Handler最终是被DispatcherServlet调用,也就是说,方式一的配置将会在Servlet中被调用。
    • 方式二,很容易理解,配置的Filter将会被springboot自动配置到Tomcat或其他web容器中。ServletContextInitializerBeans#addAdaptableBeans方法中,将自动查找spring容器中存在的Filter实现,并且根据@Order或Order来判断Filter的排序。

    冲突

    keycloak场景

    前端已经通过javascript的接口,从keycloak验证并获得了token,keycloak作为单点登录系统,理论上是可以通过token登录并验证任何一个后端服务,但是,当按照文档上面的描述,正确配置springboot及security,后端依然无法通过token的验证。但当将前后端使用同一个IP和端口时,请求正常。

    解决过程

    1. CORS同源配置。如上配置CORS,但请求依然。
    2. 打开浏览器debug,发现OPTIONS请求直接返回401,授权失败。此时如果先前已经了解CORS就不会产生疑问,CORS会将任何请求先切分为一个OPTIONS和一个原来的。
    3. 上一步表面OPTIONS请求被授权服务拦截,那么解决问题的方式就出来了。

    解决方案

    • 方案一,配置Spring security策略,不拦截OPTIONS请求

        HttpSecurity#authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
      
    • 方案二,自定义CorsFilter,设置order为最高优先级或者其他,只需要优先级比Spring security的order高便好。

    参考链接:https://www.jianshu.com/p/27060722843b

    好的博文推荐:

    spring Cors拦截流程

  • 相关阅读:
    随机森林算法参数调优
    BAYES和朴素BAYES
    阿里云 金融接口 token PHP
    PHP mysql 按时间分组 表格table 跨度 rowspan
    MySql按周,按月,按日分组统计数据
    PHP 获取今日、昨日、本周、上周、本月的等等常用的起始时间戳和结束时间戳的时间处理类
    thinkphp5 tp5 会话控制 session 登录 退出 检查检验登录 判断是否应该跳转到上次url
    微信 模板消息
    php 腾讯 地图 api 计算 坐标 两点 距离 微信 网页 WebService API
    php添加http头禁止浏览器缓存
  • 原文地址:https://www.cnblogs.com/EST-woah/p/10666560.html
Copyright © 2011-2022 走看看