1. 跨域学习
1.1 什么是跨域
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实
现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port) 任意一个不同都是不同的域:
如果跨域调用,会出现如下错误:
No ‘Access-Control-Allow-Origin’ header is present on the requested resource.Origin’http://localhost:9100’ is therefore not allowed access. The response had HTTP status code 400.
由于我们采用的是前后端分离的编程方式,前端和后端必定存在跨域问题。解决跨域问题可以采用很多种方案。下面介绍一下常见的解决方案。
1.2 常见跨域解决方案
1. JSONP
原理是:有些标签 script、img、link、iframe … 这些标签不存在跨域请求的限制,就是利用这个特点解决跨域问题。
JSONP 是服务器与客户端跨源通信的常用方法。
优点:简单适用,兼容性好(可以兼容低版本IE),
缺点:只支持 get 请求,不支持 post 请求,导致数据不安全
核心思想:网页通过添加一个 <script> 元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
2.CORS 跨域资源共享
(1) 概念
CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX只能同源使用的限制。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS接口,就可以跨源通信。
请求过程如下图:
Preflight Request(预请求):
不知道大家有没有发现,有时候我们在调用后台接口的时候,会请求两次,如下图。
其实第一次发送的就是preflight request(预检请求)。
PreflightResponse(预响应)
然后服务器端给我们返回一个PreflightResponse(预响应)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-86gBdMIX-1683703446583)(null)]
预备响应中含有响应头:
Access-Control-Allow-Credentials: true
允许客户端携带验证信息,例如 cookie 之类的。这样客户端在发起跨域请求的时候,就可以携带允许的头,还可以携带验证信息的头。
Access-Control-Allow-Headers: access-control-allow-origin
服务器允许请求中携带字段:access-control-allow-origin
Access-Control-Request-Method:GET,POST,DELETE,PUT 表明服务器允许客户端使用 POST,GET 和 OPTIONS 方法发起请求。
Access-Control-Allow-Origin: http: // localhost:63342
允许进行跨域的地址为 http: // localhost:63342 。 可以在后端配置
Access-Control-Max-Age: 3600
表明该响应的有效时间为3600秒,也就是6分钟。
更多详见技术博客:
https://www.jianshu.com/p/b55086cbd9af
(2) 使用方式呢?
1、后端接口或路由方法上添加@CrossOrigin注解。
① 放在接口上
② 放单个路由方法上:
这种实现方式相当于在路由方法里的响应体中配置响应头:
ps: 想要哪个控制器里的方法跨域,只需将下面代码复制粘贴到该方法里就行。
HttpServletResponse
response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
response.addHeader("Access-Control-Allow-Origin","*");
明显这样一个个配置是不行的。
@CrossOrigin(value = "/api",methods = {RequestMethod.POST,RequestMethod.GET})
// value表示允许跨域访问的原始路径 methods表示允许跨域的方法
// 不写就默认全部放开
我们可以使用Spring AOP的思想,对每个请求进行拦截。
③ 给spring注入一个CorsFilter (全局跨域过滤器)的方法实现
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class AA {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 1800秒
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
④ WebMvcConfigurer类
重写addCorsMappings方法
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
3.反向代理
既然不能跨域请求,那么我们不跨域就可以了,通过在请求到达服务器前部署一个服务,将接口请求进行转发,这就是反向代理。通过一定的转发规则可以将前端的请求转发到其他的服务。
通过反向代理我们将前后端项目统一通过反向代理来提供对外的服务,这样在前端看上去就跟不存在跨域一样。
反向代理麻烦之处就在原对 Nginx 等反向代理服务的配置,在目前前后端分离的项目中很多都是采用这种方式。