1.1 什么是CORS?
CORS的英文全称为(Cross-Origin Resource Sharing,跨站资源访问),它是跨域名资源访问解决方案的一种。
1.2 能够使用JSONP解决跨域为什么还要使用CORS解决跨域访问?
首先JSONP只支持Get请求,而CORS对所有HTTP请求方法(GET、POST、PUT、DELETE、OPTIONS、HEAD、TRACE)都能够支持,并且CORS还是W3C官方推荐的一种跨域资源解决方案,还有一大原因是在开发中我们可能使用到RESTful的接口架构风格而CORS能够支持所有的HTTP请求方法所以能够很好的适应,而JSONP因为只支持GET请求在一定程度上不能够满足我们的需求。
1.3 为什么会出现跨域资源无法访问的问题?
因为浏览器的资源访问策略采用的是同源访问策略(即域名完全相同(子域名也会受到影响)、端口号相同、请求协议相同),加入我们发送的是一个非简单请求,比如请求中包含了cookies,或者是Content-type不包含application/x-www-form-urlencoded,multipart/form-data或者text-plain
这个时候浏览器就会触发预检查机制发送一个OPTIONS
方法的请求到服务器,这个请求不会执行服务器端相关业务逻辑,只会返回一些HTTP头信息,浏览器可以通过返回的HTTP头信息判断是否能够发送该请求。
1.4 CORS中的HTTP头信息有哪些?
CORS中的头信息都是以Access-Control-Allow-xxxx
开头的,详情如下表:
HTTP头信息 | 解释 |
---|---|
Access-Control-Allow-Origin | 这个头部信息由服务器端返回,用来明确指定那些客户端的域名能够访问这个资源,它的定义可以为* 表示所有域名,也可以为完整的域名 |
Access-Control-Allow-Credentials | 这个头部信息只会在服务器支持通过cookies传递验证数据里。它的值只有一个就是true。跨站点带验证信息时,服务器必须要争取设置这个值,服务器才能获取到用户的cookie |
Access-Control-Allow-Headers | 它提供一个逗号分隔的列表表示服务器支持的请求类型,如果返回值中不包含要发起的请求类型那么请求就会被终止,它的值可以为* 也可以为具体的请求类型,比如application/json |
Access-Control-Expose-Headers | 这个返回信息中包含了一组头部信息,这些信息表示那些客户端可以使用。其他没有在里面的头部信息将会被限制 |
Access-Control-Allow-Methods | 它是一个由逗号分隔的列表,表明服务器支持的请求类型(比如GET 、POST 等) |
Orgin | 这个头部信息,属于请求数据中的一部分。这个值表明这个请求是从哪个浏览器打开的哪个域名下发出的。处于安全性问题,浏览器不允许我们自行修改该值 |
1.5 如何解决跨域访问问题?
-
前端可以通过设置代理的方式进行访问,如vue中axios跨域访问解决方案可参照axios解决跨域问题
-
SpringBoot后端解决跨域资源访问问题有两种方式:
- 全局定义跨域访问设置
- 定义一个
WebMvcConfig
配置类(命名可以自定义),继承WebMvcConfigurerAdapter
类,重写addCorsMappings
方法(添加跨域映射)。
package com.qingyun.house.api.config;
import com.qingyun.house.api.intercept.AuthActionIntereptor;
import com.qingyun.house.api.intercept.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Autowired
private AuthInterceptor authInterceptor;
@Autowired
private AuthActionIntereptor authActionIntereptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.excludePathPatterns("/static")
.addPathPatterns("/**");
registry.addInterceptor(authActionIntereptor)
.addPathPatterns("/accounts/profile");
super.addInterceptors(registry);
}
/**
* {@inheritDoc}
* <p>This implementation is empty.
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //拦截所有请求
.allowedOrigins("*") //放行所有域名
.allowCredentials(true)//允许发送凭证cookie信息 默认不允许发送
.allowedHeaders("*") //它用于预检,允许带有哪些请求头
.allowedMethods("GET","POST","PUT","DELTE")//允许哪些请求方法
.exposedHeaders("Header1","Header2");//暴露哪些头部信息
}
}
- 局部跨域范问支持,只需要在后端指定处理器上增加
@CrossOrigin
并添加相关属性即可
@ResponseBody
@RequestMapping(value = "/house/bookmark")
@CrossOrigin(origins = "*",allowedHeaders = "*",exposedHeaders = "Header1")
public ResultMsg houseBookmark(Long id) throws Exception {
User user=UserContext.getUser();
houseService.bindUser2House(id,user.getId(),true);
return ResultMsg.successMsg("ok");
}
测试简单跨域访问:
从上图中可以发现服务器端响应了一些之前设置的一些属性以及属性值。注意简单跨域访问无法带上cookie
。
复杂跨域访问:
- 在ajax中添加cookie信息,并开启允许传递cookie凭证,后端可以通过request对象获取cookie的值
$.ajax({
url: url,
type: 'POST',
data: data,
cache:false,
timeout:60000,
headers:{ //添加一个请求头 用于保存cookie
"ajax_cookie1":document.cookie
},
xhrFiels:{ //设置本地XHR对象的“名-值”映射
withCredentals:true //开启发送cookie凭证
}
})
后端获取cookie
//cookie的数据格式为 key=value的形式
String value=StringUtils.substringAfterLast(request.getHeader("ajax_cookie1"),"=");
//创建一个cookie
cookie=new Cookie("token",value);
- 测试发送请求
可以发现在正式发送请求之前浏览器会事先发送一个预检请求OPTIONS
方法的HTTP请求。