一、跨域概念
1. 跨域分类
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.demo.com/a.js | ||
http://www.demo.com/b.js | 同一域名,不同文件或路径 | 允许 |
http://www.demo.com/lab/c.js | ||
– | – | – |
http://www.demo.com:8000/a.js | ||
http://www.demo.com/b.js | 同一域名,不同端口 | 不允许 |
– | – | – |
http://www.demo.com/a.js | ||
https://www.demo.com/b.js | 同一域名,不同协议 | 不允许 |
– | – | – |
http://www.demo.com/a.js | ||
http://127.0.0.1/b.js | 域名和域名对应相同ip | 不允许 |
– | – | – |
http://www.demo.com/a.js | ||
http://x.demo.com/b.js | 主域相同,子域不同 | 不允许 |
http://demo.com/c.js | ||
– | – | – |
http://www.demo1.com/a.js | ||
http://www.demo2.com/b.js | 不同域名 | 不允许 |
2. 跨域解决方案
- 通过jsonp跨域
- document.domain + iframe跨域
- location.hash + iframe
- window.name + iframe跨域
- postMessage跨域
- 跨域资源共享(CORS)?
- nginx代理跨域
- nodejs中间件代理跨域
- WebSocket协议跨域
本文采用CORS后端解决跨域问题,前端无需配置:
CORS(Cross-Origin Resource Sharing)“跨域资源共享”,是一个W3C标准,它允许浏览器向跨域服务器发送Ajax请求,打破了Ajax只能访问本站内的资源限制,CORS在很多地方都有被使用,微信支付的JS支付就是通过JS向微信服务器发送跨域请求。开放Ajax访问可被跨域访问的服务器大大减少了后台开发的工作,前后台工作也可以得到很好的明确以及分工,下面我们就看讲一下如何让你的SpringBoot项目支持CORS跨域。
二、Cors跨域配置
目标:基于SpringBooot项目搭建可以站外Ajax请求访问的跨域资源服务器。
-
搭建Springboot项目
主要是配置web服务,为了简单演示,暂时用不上数据库查询。项目创建参考:【Springboot学习 | 2】jpa+mysql8.0增删改查
细节处理参考:【Springboot学习 | 3】配置使用FastJson返回Json视图 -
创建
CorsConfig全局配置
,在Controller前加上@CrossOrigin
注解,即可支持跨域访问:
CorsConfig.java
package com.springboot.three;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 全局配置
*/
@Configuration//一定得加上
public class CorsConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//配置可以被跨域的路径
.allowedOrigins("*")//允许所有的请求域名访问我们的跨域资源
.allowCredentials(true)//是否允许用户发送、处理 cookie
.allowedMethods("GET", "POST", "DELETE", "PUT")//允许所有的请求方法访问该跨域资源服务器
.maxAge(3600);//预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求
}
}
CORSConfiguration配置类继承了WebMvcConfiugrationAdaper父类并且重写了addCorsMappings方法。
属性 | 说明 | |
---|---|---|
1 | addMapping | 配置可以被跨域的路径,可以任意配置,可以具体到直接请求路径 |
2 | allowedMethods | 允许所有的请求方法访问该跨域资源服务器,如:POST、GET、PUT、DELETE等 |
3 | allowedOrigins | 允许所有的请求域名访问我们的跨域资源,可以固定单条或者多条内容,如:“http://127.0.0.1:8848”,只有这个地址及端口号才可以访问我们的跨域资源 |
4 | allowedHeaders | 允许所有的请求header访问,可以自定义设置任意请求头信息,如:“X-YAUTH-TOKEN” |
5 | maxAge | 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求 |
三、初次测试
在controller方法前加上@CrossOrigin
注解
package com.springboot.three.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
//实现跨域注解
//origin="*"代表所有域名都可访问
//maxAge飞行前响应的缓存持续时间的最大年龄,简单来说就是Cookie的有效期 单位为秒
//若maxAge是负数,则代表为临时Cookie,不会被持久化,Cookie信息保存在浏览器内存中,浏览器关闭Cookie就消失
@CrossOrigin(origins = "*",maxAge = 3600)//@CrossOrigin(origins = "http://localhost:8848", maxAge = 3600)
public class IndexController {
@RequestMapping(value = "/cors")
public String corsIndex(){
return "this is cors info";
}
}
前端测试:
这里在任意地方创建mytest项目,包含jQuery和test.html即可;我这里咋HBuilder创建效果如下,在html中只写一个ajax作为Cors跨域测试:
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>cors-demo</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<script type="text/javascript" src="js/jquery-3.4.1.min.js" ></script>
</head>
<body>
<input type="button"value="测试" οnclick="ajaxloding()" />
<div id="corsmessage"></div>
<script>
var getdata=0;
function ajaxloding(){
$.ajax({
async:false,
type:"get",
url:"http://localhost:8080/cors",
contentType: "application/x-www-form-urlencoded",
dataType: "json",
data: {},
success:function(result){
console.log(result);
getdata=result
},
error: function (errorMsg) {
//请求失败时执行该函数
alert("请求数据失败!");
}
});
$("#corsmessage").text(getdata)
}
</script>
</body>
</html>
运行结果:
- 提前运行springboot,端口在8080
- 在HBuilder端运行默认端口为8848,和springboot部署的8080满足跨域要求(如果没有HBuilder,可以在局域网下,另一台电脑打开html,端口不一致实现测试要求)
- 点击button,成功实现跨域:返回信息。
如果注释掉controller中的@CrossOrigin
标签,会显示被cors拒绝访问。
四、cors跨域原理解析
一个 CORS 配置用一个CorsConfiguration
类来表示,它的定义如下:
public class CorsConfiguration {
private List<String> allowedOrigins;
private List<String> allowedMethods;
private List<String> allowedHeaders;
private List<String> exposedHeaders;
private Boolean allowCredentials;
private Long maxAge;
}
Spring`中对 CORS 规则的校验,都是通过委托给DefaultCorsProcessor实现的。
DefaultCorsProcessor 处理过程如下:
- 判断依据是
Header
中是否包含Origin
。如果包含则说明为CORS
请求,转到 2;否则,说明不是 CORS 请求,不作任何处理。 - 判断
response
的Header
是否已经包含Access-Control-Allow-Origin
,如果包含,证明已经被处理过了, 转到 3,否则不再处理。 - 判断是否同源,如果是则转交给负责该请求的类处理
- 是否配置了 CORS 规则,如果没有配置,且是预检请求,则拒绝该请求,如果没有配置,且不是预检请求,则交给负责该请求的类处理。如果配置了,则对该请求进行校验。
校验就是根据 CorsConfiguration这个类的配置进行判断:
- 判断
origin
是否合法 - 判断
method
是否合法 - 判断
header
是否合法 - 如果全部合法,则在
response header
中添加响应的字段,并交给负责该请求的类处理,如果不合法,则拒绝该请求。
ps.查资料时发现,如果
allowedOrigins("*")
,无法使用cookies,具体还没验证,等用到时再回来补充;
解决方法参考:跨域丢失cookie的问题