相信大部分前后端分离的项目中,都会经常碰到跨域的问题,刚开始写程序时,对这个也是很好奇,是为什么呢?随着对应用程序请求的原理了解的加深,可以总结出多种方式来解决这个问题。
什么是跨域
(资料图片仅供参考)
首先要了解下浏览器的同源策略,同源通常是指以下三个相同:
协议相同(如http,https)域名相同(二级域名与主域名也不是同一个域名)端口相同(同一个域名,不同端口也算不同)
如 http://example.com 与 https://example.com =>不同
http://a.example.com 与 http://b.example.com =>不同
http://example.com:8000 与 http://example.com:8001 =>不同
同源策略是浏览器为了保证用户的信息安全,防止其他恶意网站来窃取数据,最常见的是web 系统中的cookie,而这些cookie通常是保存了比较多的用户信息,如登录状态,用户信息等,如果其他网站可以任意去使用这些数据,那对于应用本身也是一种安全漏洞。
正因为有同源策略的限制,非同源的应用,是不能互相读取 cookie、LocalStorage、Dom、Ajax 请求不能发送。但是在现在微服务盛行的年代,前后端分离的架构下,跨应用之间的访问是很常见的,要如何解决这种情况呢?
CORS即跨域资源共享,是W3C的标准,一种基于http 协议请求头的机制,需要浏览器与服务器的支持,整个过程是浏览器与服务器自动完成。
浏览器根据请求的内容,将请求分成两种请求:简单请求、非简单请求
对于请求方法为HAED、GET、POST的请求,如果它的请求头是 Accept、Accept-Language 或者请求头是 Content-Type 并且内容是 application/x-www-form-urlencoded、multipart/form-data、text/plain, 满足以上的即为简单请求,其他的即为非简单请求。
一些简单请求可能会包含以上的内容之外的内容,比较常见的就是cookie,需要增加规范外的用户凭证相关的内容,Http规范里,需要服务器返回 Access-Control-Allow-Credentials: true,这样客户端才可以提交cookie上去。
非简单请求是对服务器的特殊请求,如PUT、DELETE或者是 content-type 为 application/json,非简单请求会发起一个预检请求,询问服务器当前所有的域名是否在服务器允许的范围,同时也会返回服务器允许的http请求方法和请求头字段。
预检请求会发起 OPTIONS的请求,来询问服务器,在请求头里,主要是用Origin 来标识来源,除此之外还有两个字段:
Access-Control-Request-Methods 标识当前用到的HTTP MethodAccess-Control-Request-Headers 标识当前请求会使用的请求头,比如自定义请求头字段的x-req-id
服务端响应预检的请求内容包括几个字段
Access-Control-Allow-Origin 允许哪些源来请求当前的服务器Access-Control-Allow-Methods 允许请求的HTTP方法Access-Control-Allow-Headers 允许请求的请求头Access-Control-Allow-Credentials Access-Control-Max-Age 指定当前预检请求的有效期,单位秒
服务器如果对于来源的Origin、Access-Control-Request-Methods、Access-Control-Request-Headers 的内容检测通过,则会将响应的字段返回给以上的字段中,浏览器会比较响应的字段来判断是否允许发起请求。
实践
静态页面