关于spring:跨站资源共享CORS原理深度解析

36次阅读

共计 4136 个字符,预计需要花费 11 分钟才能阅读完成。

我置信如果你写过前后端拆散的 web 应用程序,或者写过一些 ajax 申请调用,你可能会遇到过 CORS 谬误。

  • CORS 是什么?
  • 它与安全性无关吗?
  • 为什么要有 CORS?它解决了什么目标?
  • CORS 是怎么运行的?

如果您有这些问题,那么这篇文章非常适合您。

一、什么是 CORS?

要理解什么是 CORS(Cross-Origin Resource Sharing:跨站资源共享),首先咱们须要理解什么是同源策略 Same Origin Policy(SOP)。SOP 是所有的古代浏览器都具备的安全措施,它不容许从一个加载的 js 脚本和资源的 Origin 域与另一个 Origin 域进行交互。换句话说,如果您的网站是www.example.com,则您无奈向www.test.com 收回 XHR 申请。

那么 SOP 有什么用?如果没有同源策略的限度,你想想会产生什么? 比方:您曾经登录到微博,并且不小心关上了一个歹意网站。该网站能够向微博发出请求,并从您微博登录的会话中提取个人信息。这显然是微小的平安问题,为了避免这种状况,在浏览器中施行同源策略的限度。实际上,服务器并没有意识到在浏览器端产生的这所有,您依然能够应用 curl 或 postman 收回雷同的申请,并且所有响应失常,因为这些工具上没有 SOP。

如果说 SOP 是限度跨源拜访的一种形式,那么 CORS 是一种绕过 SOP 限度并容许您的前端向服务器提出非法申请的办法。 如果您的服务端确实是存在跨域的状况(实际上对于古代分布式应用,这很常见),因为 SOP 限度您的客户端将无奈向多节点跨域服务器收回 xhr 申请。救星就呈现了,CORS 使咱们可能以平安且可治理的形式做到跨域申请,冲破同源策略的限度。

二、同源策略的源(Same Origin Policy 的 Origin)

源由三局部组成:协定,hostip(域)和端口。例如

  • http://example.com/xxx/index.htmlhttp://example.com/yyy/index.html 是同源,
  • http://example.com:80http://example.com(对于 http 默认端口为 80)是同源。
  • 因为协定不同,http://example.com/app1https://example.com/app2 是不同的源。
  • http://example.comhttp://www.example.com因为域名不同,也是不同的源
  • 十分要留神的是 http://localhosthttp://127.0.0.1 是不同的源

同源策略就是:不容许不同的 ip、端口、协定的利用在浏览器内进行相互资源共享、申请调用。

三、CORS 如何运作?

CORS 标准容许服务器向浏览器返回一些 HTTP Headers,浏览器能够基于这些 HTTP Headers 来决定是否冲破 SOP 的限度。最次要的一个 HTTP Headers 是 Access-Control-Allow-Origin。

// 指标服务容许所有的网站对其进行跨域拜访
Access-Control-Allow-Origin: * 
// 指标服务容许特定的网站对其进行跨域拜访
Access-Control-Allow-Origin: https://example.com

CORS 有两种类型的申请:“simple”简略申请和“preflight”预检申请,依据申请办法的不同由浏览器确定应用哪种申请。

simple 简略申请:

如果合乎以下所有条件,则 API 申请被视为简略申请:

  • API 办法是以下办法之一:GET,POST 或 HEAD。
  • Content-Type申请头蕴含:application/x-www-form-urlencodedmultipart/form-datatext/plain

这两个条件将形成大多数简略申请的用例,然而能够在此处找到更具体的简略申请条件列表。

如果您的 API 申请被视为 simple 简略申请,这个申请就能够间接被发送给服务器。服务器应用 CORS HTTP Headers 进行响应,浏览器将查看 Access-Control-Allow-Origin 后决定这个申请是否能够冲破同源策略的限度,进行下一步的解决。

preflight 预检申请:

如果您的 API 申请不满足成为简略申请的规范(最常见不满足简略申请规范的 Content-Type 值为application/json),则浏览器将在发送理论申请之前收回预检申请。

举一个例子,咱们尝试应用 GET 申请 https://example.com/statusContent-Typeapplication/json,所以浏览器认为它不合乎一个简略申请的规范,因而浏览器会在收回理论申请之前收回预检申请,这个预检申请是应用 HTTP 的 OPTIONS 办法收回的:

curl --location --request OPTIONS 'http://example.com/status' \
--header 'Access-Control-Request-Method: GET' \
--header 'Access-Control-Request-Headers: Content-Type, Accept' \
--header 'Origin: http://test.com'

下面的 curl 就是模仿预检申请,理论作用是:浏览器心愿通知服务器,我的理论申请将应用 HTTP GETmethod 进行调用,Content-TypeAccept 作为 HTTP headers,这个申请是从 https://test.com 发动的。服务器响应此申请:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS, GET, HEAD, POST
Access-Control-Allow-Headers: Content-Type, Accept
  • Access-Control-Allow-Origin:容许发出请求的源,或者 * 能够从任何起源发出请求。(即容许跨域的源)
  • Access-Control-Allow-Methods:容许的以逗号分隔的 HTTP 办法列表。(即容许跨域的 HTTP 办法)
  • Access-Control-Allow-Headers:容许发送的 HTTP headers 列表。

浏览器收到服务端的预检申请响应之后,在咱们的示例中服务器响应 * 能够从任何起源发出请求,因而当初浏览器将再次拜访 https://example.com/status,应用 GET 办法(不再是 OPTIONS 办法),浏览器将不再限度该申请的收回与响应数据的接管。
如果预检申请响应的 Origin 是特定的 Access-Control-Allow-Origin: http://domain.com,浏览器将呈现Cross-Origin Request Blocked 谬误。因为服务器端预检后果只容许 http://domain.com 收回跨域申请,不容许其余利用向我收回跨域申请。

四、如何解决 CORS 谬误

咱们当初晓得什么是 CORS 及其工作原理,前面的事件其实就简略了。从下面的内容咱们须要留神的是,对 CORS 的齐全控制权在服务器,即服务器能够容许或禁止源的跨域拜访。所以说跨域问题的解决个别都在服务端进行, 不同的服务端的解决 HTTP 申请头的代码是不一样的,当然也能够不必写代码,比方:nginx、haproxy 设置。然而万变不离其宗:最终都是对 HTTP Headers 进行重写

我就简略的举几个例子:

比方 Servlet 解决跨域

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) resp; 
        response.setHeader("Access-Control-Allow-Origin", "*"); // 解决跨域拜访报错   
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");   
        chain.doFilter(req, resp); 
}

比方 Spring MVC 配置

@Configuration
public class GlobalCorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**")    // 增加映射门路,“/**”示意对所有的门路履行全局跨域拜访权限的设置
                        .allowedOrigins("*")    // 凋谢哪些 ip、端口、域名的拜访权限
                        .allowCredentials(true)  // 是否容许发送 Cookie 信息 
                        .allowedMethods("GET","POST", "PUT", "DELETE")     // 凋谢哪些 Http 办法,容许跨域拜访
                        .allowedHeaders("*")     // 容许 HTTP 申请中的携带哪些 Header 信息
                        .exposedHeaders("*");   // 裸露哪些头部信息(因为跨域拜访默认不能获取全副头部信息)}
        };
    }
}

欢送关注我的博客,外面有很多精品合集

本文转载注明出处(必须带连贯,不能只转文字):字母哥博客 – zimug.com

感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源!。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。

  • 《手摸手教你学 Spring Boot2.0》
  • 《Spring Security-JWT-OAuth2 一本通》
  • 《实战前后端拆散 RBAC 权限管理系统》
  • 《实战 SpringCloud 微服务从青铜到王者》
  • 《VUE 深入浅出系列》

正文完
 0