同源策略和跨域解决方案 CORS
Posted 陈皮的JavaLib
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同源策略和跨域解决方案 CORS相关的知识,希望对你有一定的参考价值。
文章目录
同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
浏览器的同源策略它会阻止读取来自不同源的资源。同源策略机制主要用于阻止恶意站点读取另一个站点的数据,让用户安全地上网。
同源策略是为了保护本地数据不被 javascript 代码获取回来的数据污染,拦截的是客户端对于响应的接收,即浏览器请求发送出去了,服务器也响应了,但是响应无法被浏览器接收。
同源指协议,域名,端口都需要相同。例如相对于 https://www.chenpi.com/index.html 。
- https://www.chenpi.com/user/add:同源。
- http://www.chenpi.com/index.html:不同源,协议不同。
- https://www.chenpi.cn/index.html:不同源,域名不同。
- https://www.chenpi.com:8081/index.html:不同源,端口不同。
我们打开百度的页面,然后打开控制台(按 F12),输入以下命令请求京东的首页地址。发现出现了跨域错误。
fetch("https://www.jd.com/")
通过查看请求详情,请求是正常发送出去了,而且服务端也成功响应了,但是请求结果没被浏览器接收。
因为浏览器同源策略的存在,所以才出现跨域限制问题。但有时我们不需要这种跨域限制,CORS 就是解决跨域的一种解决方案。还有另外一种跨域解决方案是 JSONP,但是这种只支持 GET 请求,比较受限。
CORS 简介
CORS(Cross-Origin Resource Sharing,跨域资源共享),是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制。
CORS 原理就是在进行访问跨域资源时,浏览器和服务器通过自定义的 HTTP 头部进行协商沟通,从而决定请求或者响应是应该成功还是失败。CORS 需要浏览器和服务器同时支持。几乎所有浏览器都支持此功能,IE 浏览器版本不能低于 IE10。
CORS 请求模型
CORS 分为两种请求模型:
- 简单请求:支持 head/get/post 请求,请求头信息只能是
Accept,Accept-Language,Content-Language,Last-Event-ID,Content-type
等标准头部,不支持自定义 header,content-type 值只支持text/plain,application/x-www-urlencoded,multipart/form-data
。请求不可以携带 cookie。 - 非简单请求,协商模型/预检请求(Preflighted Request):除了简单请求之外,其他就是属于非简单请求。
这两种请求模型的通信过程如下:
简单请求:
- 浏览器发出的请求添加请求头部 Origin,标识请求页面的源信息(协议,域名,端口号),例如
Origin: https://www.chenpi.com
。 - 服务器接收到请求,可检查 Origin 判断此源是否可信任。如果信任允许此源请求服务器的资源,就在响应头中返回
Access-Control-Allow-Origin: https://www.chenpi.com
。如果是请求的资源是公共资源可直接返回Access-Control-Allow-Origin: *
表示允许所有请求源。 - 浏览器根据响应头部结果,决定是否接收响应数据。
- 默认情况下,请求不可以携带 cookie,如果需要携带,ajax 请求需要设置 xhr 属性 withCredentials 的值为 true,服务器需要设置响应头
Access-Control-Allow-Credentials: true
。
非简单请求:
- 例如浏览器发出 PUT 请求,浏览器会预先发出 OPTIONS 请求,携带请求头
Origin: https://www.chenpi.com,Access-Control-Request-Method: PUT,Access-Control-Request-Headers: 自定义的头部字段,多个头部以逗号分隔(可选)
。 - 服务器接收到 OPTIONS 请求,根据请求头信息决定是否响应此请求,如果信任可以在响应头中添加如下信息:
- Access-Control-Allow-Origin:https://www.chenpi.com
- Access-Control-Allow-Methods: PUT(允许的方法,多个方法以逗号分隔)
- Access-Control-Allow-Headers: 允许的头部字段,多个方法以逗号分隔。
- Access-Control-Max-Age: 预检请求(OPTIONS)可以缓存的最长时间,单位秒。
- 预检请求被允许之后,以后每次浏览器正常的 CORS 请求(例如 PUT),就和简单请求过程一样了。
Spring Boot 实现 CORS
显示设置头部
通过 CORS 原理可知,我们只需要在响应头部中添加相应头部信息即可解决跨域问题,如下:
package com.chenpi.controller;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author 陈皮
* @Date 2022/5/1
* @Version 1.0
*/
@RestController
@RequestMapping
public class ChenPiController
@GetMapping("test")
public JSONObject test(HttpServletResponse httpServletResponse)
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "陈皮");
jsonObject.put("age", 18);
// 解决跨域
httpServletResponse.setHeader("Access-Control-Allow-Origin", "https://www.baidu.com");
// httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
return jsonObject;
如果需要允许携带 cookie 信息,请求头部和响应头部都添加相应信息。注意,当添加Access-Control-Allow-Credentials: true
时,Access-Control-Allow-Origin
的值不能设置为星号。
package com.chenpi.controller;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author 陈皮
* @Date 2022/5/1
* @Version 1.0
*/
@RestController
@RequestMapping
public class ChenPiController
@GetMapping("test")
public JSONObject test(HttpServletResponse httpServletResponse)
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "陈皮");
jsonObject.put("age", 18);
// 解决跨域
httpServletResponse.setHeader("Access-Control-Allow-Origin", "https://www.baidu.com");
// 允许携带cookie,浏览器跨域使用验证:fetch('http://127.0.0.1:8080/test',mode: 'cors',credentials: 'include')
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
return jsonObject;
注解 @CrossOrigin
对于以上方案,如果要解决跨域的接口比较多,就需要在每个接口都写重复代码。Spring Boot 提供了跨域解决方案,使用注解@CrossOrigin
。
@CrossOrigin 注解可作用在单个接口上,或者作用在类上(对类下的所有接口都生效)。
package com.chenpi.controller;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author 陈皮
* @Date 2022/5/1
* @Version 1.0
*/
@RestController
@RequestMapping
public class ChenPiController
@GetMapping("test")
@CrossOrigin
public JSONObject test()
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "陈皮");
jsonObject.put("age", 18);
return jsonObject;
对于 CORS 通信过程用到的 HTTP 头部,@CrossOrigin 注解里面都定义了相应属性,并设置了默认值。
@Target(ElementType.TYPE, ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin
// 默认是*
@AliasFor("origins")
String[] value() default ;
@AliasFor("value")
String[] origins() default ;
// 通配符,默认空
String[] originPatterns() default ;
// 默认所有自定义头部都被允许,*
String[] allowedHeaders() default ;
// 默认允许方法和接口定义的请求方式保持一致,例如@GetMapping("test")则值为GET
RequestMethod[] methods() default ;
// 默认,Access-Control-Allow-Credentials:false
String allowCredentials() default "";
// -1代表默认值1800秒,Access-Control-Max-Age:1800
long maxAge() default -1;
我们可以修改默认值,并且此注解可以在类中和方法中同时定义,它们会形成互补,即对任意一个头部属性,如果在方法的注解中是默认值,则会再去类上的注解中进行判断,如下:
package com.chenpi.controller;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author 陈皮
* @Date 2022/5/1
* @Version 1.0
*/
@RestController
@RequestMapping
@CrossOrigin(origins = "https://www.baidu.com", "https://www.jd.com", allowCredentials = "true"
, maxAge = 3600)
public class ChenPiController
@GetMapping("test")
public JSONObject test()
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "陈皮");
jsonObject.put("age", 18);
return jsonObject;
@GetMapping("demo")
@CrossOrigin(origins = "https://www.taobao.com", allowedHeaders = "ChenPiHeader",
allowCredentials = "false")
public JSONObject demo()
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "陈皮");
jsonObject.put("age", 20);
return jsonObject;
全局配置
可以进行全局配置,这样就不需要对每个接口进行单独跨域配置,如下:
package com.chenpi.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer
@Override
public void addCorsMappings(CorsRegistry registry)
registry.addMapping("/**").allowedOrigins("https://www.baidu.com", "https://www.jd.com")
.allowCredentials(true).allowedHeaders("chenpi-header")
.allowedMethods("PUT", "GET", "POST", "HEAD")
.maxAge(3600);
本次分享到此结束啦~~
如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!
以上是关于同源策略和跨域解决方案 CORS的主要内容,如果未能解决你的问题,请参考以下文章