一. 什么是跨域
1. 同源策略
同源策略是 游览器(服务器之间的互相访问不存在跨域) 的
一种安全约定,为防止收到XSS、CSRF攻击,不同域之间请求资源,都算作跨域。
协议、子域名、主域名、端口号任意一个不同,即被游览器认作不同域
(即使是同一网址 IP 和域名也看做不同域,例如:127.0.0.1 和 localhost)
2. 同源策略限制
不受限:
(1)跳转链接、重定向、表单提交
(2)跨域资源加载(< img >、< link >、< script >、< iframe > 等),但不允许通过 JS 读写加载的内容
受限:
(1)Cookie、LocalStorage、IndexedDB 等存储性内容
(2)DOM 节点
(3)AJAX异步请求
3. 为什么有跨域限制
我们用银行页面举例
(1)DOM
黑客可以在恶意页面中用 iframe 嵌套一个银行的页面
用户在这个银行页面中输入账号密码,黑客就可以通过DOM节点获取数据
(2)CSRF攻击(跨站请求伪造)
用户的游览器中存有银行的 cookie
当用户访问恶意页面时,黑客通过 AJAX 异步请求,携带用户的银行 cookie 像银行发送请求,造成数据泄露
二、跨域解决方案
1. JSONP (已逐渐淘汰)
JSONP 利用的是游览器允许 < script > 标签跨域加载资源的机制
事先在客户端定义好回调函数
然后通过< script > 标签请求服务器,服务器返回一个执行回调函数的 JS 脚本并携带参数
优点:
使用便捷,跨平台
缺点:
只能支持 get 请求,容易受到 XSS 攻击
2. CORS(跨域资源共享)
CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,基本思想就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是否成功。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
(1) 简单请求 / 非简单请求 及其预检机制
简单请求:
简单请求需同时满足两个条件
条件1:get、head、post 请求
条件2:Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
简单请求不会触发预检机制,游览器先执行请求再判断是否跨域
非简单请求:
不满足简单请求条件的均为非简单请求,例如 put、delete 请求
非简单请求在发送前,游览器会先发起一次 “预检请求”,从服务端得知是否允许该跨域,若允许再进行正常通信
(2)springboot 实现跨域
方案1: 使用 @CrossOrigin 注解(需要跨域的类或者方法上)
@CrossOrigin
@RequestMapping("/demo")
@RestController
public class DemoController {
}
方案2: 实现 WebMvcConfigurer 接口
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(1800)
.allowedHeaders("*");
}
}
方案3: 使用过滤器在 response 中写入响应头
@WebFilter(filterName = "CorsFilter")
@Configuration
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "1800");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}
3. WebSocket
Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也支持跨域通信。