CORS(跨域资源共享)笔记
0.前言
CORS(Cross-Origin Resource Sharing)是一个用于处理跨域问题的W3C标准,本文将介绍什么是跨域,引起跨域的同源策略,什么是CORS,CORS的工作过程,请求方式以及与它有关的header,最后是java使用例子代码。
1.什么是跨域?
浏览器从一个域名的网页请求另一个域名的资源时,域名,端口,协议任意一个不同都是跨域请求。(需要注意域名和域名对应的ip也属于跨域)
2.什么是同源策略?
在Web浏览器中,允许某个网页脚本访问另一个网页的数据,但前提是这两个网页必须有相同的协议,主机名和端口号,一旦两个网站满足上述条件,这两个网站被认为同源。此策略可防止某个网页上的恶意脚本通过该网页的文档对象模型访问另一个网页上的敏感数据,但是仅适用于脚本,不适用于HTML标签。
3.什么是CORS?
CORS是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),允许第一个资源所在的域之外的另一个域请求web页面上的受限资源。尤其是Ajax请求,在默认情况下是被同源安全策略禁止的。CORS定义了一种浏览器和服务器可以交互的方式,以确定允许跨源请求是否安全。它比纯粹的同源请求允许更多的自由和功能,但比简单的允许所有跨源请求更安全。整个CORS通信过程,都是浏览器自动完成的,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加头信息,有时会多一次请求,但用户不会有感觉,因此实现CORS通信的关键在服务器,只要服务器实现了CORS接口,就可以进行跨源通信。
4.CORS的工作过程
对于可以修改数据的Ajax和HTTP请求方法(通常为GET以外的HTTP方法或者用于某些MIME类型的POST使用),规范要求浏览器预先发送请求,使用HTTP OPTIONS请求方法从服务器请求支持的方法,在得到服务器的“批准”后,使用实际的HTTP请求方法发送实际请求。服务器还可以通知客户端“凭据”(包括Cookies和HTTP身份验证数据)是否应随请求一起发送。
5.CORS请求
CORS请求分为简单请求和预检请求,上图中发送的真实请求为简单请求,发送真实请求前需进行的OPTIONS请求为复杂请求。
①简单请求
概念:当请求方式为HEAD,GET或POST并且HTTP头信息仅包含Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type,其中Content-Type仅限三个值,包括application-x-www-form-urlencoded,multipart/form-data,text-plain。(这样设计是为了兼容form表单,因为表单一直可以发送跨域请求。)
例子:加入用户访问http://www.example.com,并且跨域获取http://service.example.com的用户信息。与CORS兼容的浏览器将尝试向service.example.com进行跨域请求,具体如下:
//浏览器添加一个额外的Origin头,值为为父页面提供服务的域,并发送GET请求 Origin: http://www.example.com //在service.example.com域的服务将返回带Access-Control-Allow-Origin头,其值表示允许访问的域或者返回错误页面,表示服务器不准跨域请求。 Access-Control-Allow-Origin: http://www.example.com Access-Control-Allow-Origin: * //通配符表示允许所有
[注:“*”的值很特殊,因为它不允许请求提供凭据,这意味着它不允许在跨域请求中发送HTTP身份验证,客户端SSL证书或Cookie。在CORS体系结构中,Access-Control-Allow-Origin头是由外部web服务设置的(service.example.com),而不是原始的web应用服务(www.example.com)设置的]
②预检请求
概念:除了上面简单请求的情况外都要先进行预检请求,通过后再发送真实请求。
例子:
//www.example.com域上的服务发出OPTIONS预检请求 OPTIONS / Host: service.example.com Origin: http://www.example.com //如果service.example.com接受该操作,则响应如下: Access-Control-Allow-Origin: http://www.example.com Access-Control-Allow-Methods: PUT, DELETE //之后www.example.com将发送真实请求,若不接受,将会对OPTIONS请求响应错误。
6.与CORS有关的头
①请求头
Origin
说明:该字段必须,指示了请求来自于哪个站点。该字段仅指示服务器名称,并不包含任何路径信息
语法:Origin: ""
Origin: <scheme> "://" <host> [ ":" <port> ]
示例:Origin: http://www.example.com
Access-Control-Request-Method
说明:该字段必须,用于预检请求告诉服务器哪些HTTP方法在实际请求是将被使用
语法:Access-Control-Request-Method: <method>
示例:Access-Control-Request-Method: POST
Access-Control-Request-Headers
说明:用于预检请求告诉服务器在真正的请求中会采用哪些头
语法:Access-Control-Request-Headers: <header-name>, <header-name>, ...
示例:Access-Control-Request-Headers: X-PINGOTHER, Content-Type
②响应头
Access-Control-Allow-Origin
说明:该字段必须,用于指定该响应是否被允许与给定的域共享
语法:Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origin>
示例:Access-Control-Allow-Origin:* --表示允许所有资源访问
Access-Control-Allow-Origin:http://www.example.com --表示允许http://www.example.com访问
Access-Control-Allow-Credentials
说明:该字段可选。值为布尔值,表示是否允许发送Cookie,默认情况下,Cookie不包括在CORS请求之中。
语法:Access-Control-Allow-Credentials:true
Access-Control-Expose-Headers
说明:该字段可选,用于列出哪些头部可以作为响应的一部分暴露给外部。默认情况下有七种简单响应头部可以暴露给外部,包括:Cache-Control,Content-Language,Content-Length,Content-Type,Expires,Last-Modified,Pragma,Access-Control-Max-Age,Access-Control-Allow-Methods,Access-Control-Allow-Headers
语法:Access-Control-Expose-Headers: <header-name>, <header-name>, ...
示例:Access-Control-Expose-Headers: Content-Length
Access-Control-Max-Age
说明:该字段可选,表示预检请求的返回结果(即Access-Control-Allow-Method和Access-Control-Allow-Headers提供的信息)可以被缓存多久,即多久可以不用发送预检请求,若为-1则禁用,表示每次都请求都必须发送预检请求。
语法:Access-Control-Max-Age: <delta-seconds>
示例:Access-Control-Max-Age: 600
Access-Control-Allow-Methods
说明:该字段必须,在对预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
语法:Access-Control-Allow-Methods: <method>, <method>, ...
示例:Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers
说明:如果请求包括Access-Control-Request-Headers字段,则该字段必须,表明服务器支持的所有header,不限于预检中请求发送的字段还可能有自定义的。
语法:Access-Control-Allow-Headers: <header-name>[, <header-name>]*
Access-Control-Allow-Headers: *
示例:Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
7.java操作
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; // 这里填写你允许进行跨域的主机ip httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); // 服务器允许的header httpServletResponse.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); // 允许的访问方法 httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); // Access-Control-Max-Age 用于 CORS 相关配置的缓存 httpServletResponse.setHeader("Access-Control-Max-Age", "1209600"); // 是否允许发送cookie httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; if (null != httpRequest && "OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) { return; } filterChain.doFilter(servletRequest, httpServletResponse); }