一 、跨域是啥 为啥会发生?
跨域本质是浏览器基于同源策略的一种安全手段,是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。浏览器执行JavaScript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
理解点:1.服务器之间的请求是不存在跨域问题的!
2.当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
3.localhost和127.0.0.1虽然都指向本机,但也属于跨域.
4.无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
5.无法接触非同源网页的 DOM
6.无法向非同源地址发送 AJAX 请求
二、哪些操作不受同源策略限制
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的;
- 跨域资源的引入是可以的。但是
JS
不能读写加载的内容。如嵌入到页面中的<script src="..."></script>
,<img>
,<link>
,<iframe>
等。
三、 如何解决?
Nginx VS CORS
简单来说,Nginx是间接跨域,而CORS则实现了直接跨域。Nginx的反向代理“欺诈了”浏览器,所以浏览器和服务器都认为是同源访问,所以Session不会丢失。(PS:如果发生跨域访问,服务器会每次都创建新的Session,所以才造成了前后端分离的Session丢失问题。) 至于CORS这种跨域机制的安全性和灵活性更高,但需要自己解决跨域访问Session丢失的问题,通常情况可以采用Session+Redis来实现Session共享。)
1.Nginx
如果是通过vue-cli
脚手架工具搭建项目,我们可以通过webpack
为我们起一个本地服务器作为请求的代理对象通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域,在vue.config.js
文件,新增以下代码
amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
open: true,// vue项目启动时自动打开浏览器
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}
项目部署在服务器上需要修改nginx.conf
server {
listen 80;
# server_name xxx.xxx.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
真正的跨域操作由Nginx的proxy_pass进行完成,并成功将验证码信息以代理的身份返回给浏览器,让浏览器处于同源访问后台的错觉。打开F12可以看到代理服务器:
2.CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享)是一种W3C标准,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应
CORS
实现起来非常方便,只需要增加一些 HTTP
头,让服务器能声明允许的访问来源
只要后端实现了 CORS
,就实现了跨域
SpringBoot解决
【方式一】全局配置
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有的当前站点的请求地址,都支持跨域访问
.allowedOrigins("*") // 所有的外部域都可跨域访问。 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") // 当前站点支持的跨域请求类型是什么
.allowCredentials(true) // 是否支持跨域用户凭证
.maxAge(3600) // 超时时长设置为1小时。 时间单位是秒。
.allowedHeaders("*"); //获取所有请求头字段
}
}
WebMvcConfigurerAdapter在Spring5.0已经被标记为Deprecated,推荐使用过滤器
基于过滤器的方式,方式简单明了,就是在response中写入这些响应头
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@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", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}
【方式二】@CrossOrigin
spring 4.2 及以上版本为CORS开箱即用提供了很好的支持,与典型的基于过滤器的解决方案相比,它提供了一种更容易、更强大的配置CORS的方法。只需在 controller 的类名、或方法名上加 @CrossOrigin 注解即可
@CrossOrigin
细粒度单个请求控制
public class GoodsController {
@CrossOrigin(origins = "http://localhost:8181")//只对某一ip有效
@GetMapping("/list")
public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {}
}