如果前后端不分离,是不是就不会有跨域问题呢?😭
一、同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了, ,但是无法被浏览器接收。
我们在简书首页请求百度,可以发现跨域的报错blocked by CORS policy:
fetch(new Request('https://www.baidu.com', {
method: 'POST',
headers: {},
body: "param1=value1¶m2=value2"
})).then((resp) => { console.log(resp) })
同源策略,它是由Netscape提出的一个著名的安全策略。只有当 protocol(协议)、domain(域名)、port(端口)三者一致时才是同源,后面的请求路径、请求参数、锚点可以不一致。
二、跨域资源共享(CORS)
当我们在响应头中添加了允许跨域header,浏览器就不会拒绝请求了。
- Access-Control-Allow-Origin: 允许哪些ip或域名可以跨域访问
- Access-Control-Max-Age: 表示在多少秒之内不需要重复校验该请求的跨域访问权限
- Access-Control-Allow-Methods: 表示允许跨域请求的HTTP方法,如:GET,POST,PUT,DELETE
- Access-Control-Allow-Headers: 表示访问请求中允许携带哪些Header信息,如:Accept、Accept-Language、Content-Language、Content-Type
三、实际如何解决跨域问题
1、在前端服务解决跨域
1.1 html标签
虽然浏览器对于不符合同源策略的访问是禁止的,但是仍然存在例外的情况,如以下资源引用的标签不受同源策略的限制:
- html的script标签
- html的link标签
- html的img标签
- html的iframe标签
1.2 JSONP
JSONP 主要就是利用了 script 标签没有跨域限制的这个特性来完成的。浏览器是允许像 link、img、script 标签在路径上加载一些内容进行请求,是允许跨域的,JSONP 的实现原理就是在 script 标签里面加载了一个链接,去访问服务器的某个请求。但是,,实际应用中并不推荐。
2、在后端服务解决跨域
2.1 设置HttpServletResponse响应请求头(局部跨域配置)
这是最原始也是最基础的方法。
@RequestMapping("/cors")
@ResponseBody
public String cors(HttpServletResponse response){
//使用HttpServletResponse定义HTTP返回请求头
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
return "cors";
}
2.2 使用CrossOrigin注解(局部跨域配置)
利用SpringMvc提供的CrossOrigin注解,原理同上,只是利用注解切面的方式实现。
@RequestMapping("/cors")
@ResponseBody
@CrossOrigin(origins = "http://localhost:8080", maxAge = 600)
public String cors( ){
return "cors";
}
2.3 使用CorsFilter进行(全局跨域配置)
一个个的加注解实际开发不可取,所以定义过滤器,实现统一处理,是最常用的手段。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//开放哪些ip、端口、域名的访问权限,星号表示开放所有域
config.addAllowedOrigin("*");
//是否允许发送Cookie信息
config.setAllowCredentials(true);
//开放哪些Http方法,允许跨域访问
config.addAllowedMethod("GET","POST", "PUT", "DELETE");
//允许HTTP请求中的携带哪些Header信息
config.addAllowedHeader("*");
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
config.addExposedHeader("*");
//添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource);
}
}
2.4 重写WebMvcConfigurer的addCorsMappings方法(全局跨域配置)
这个也是SpringMvc提供的,相对过滤器性能会更高。
@Configuration
public class GlobalCorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
.allowedOrigins("*") //开放哪些ip、端口、域名的访问权限
.allowCredentials(true) //是否允许发送Cookie信息
.allowedMethods("GET","POST", "PUT", "DELETE") //开放哪些Http方法,允许跨域访问
.allowedHeaders("*") //允许HTTP请求中的携带哪些Header信息
.exposedHeaders("*"); //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
}
};
}
}
3、在 Nginx 等反向代理解决跨域
原本浏览器是访问 localhost:8080/api请求后端服务的接口,现在让 Nginx 监听 8080 端口,把请求转发到后端服务新的端口 80801 上。Nginx 接收到后端服务的响应后,添加相关的 CORS 头部返回给浏览器,就不会报跨域的问题。
同理,转发对应的protocol(协议)、domain(域名)也是可行的。
events {}
http {
server {
listen 8080;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'http://127.0.0.1:8080';
add_header 'Access-Control-Allow-Methods' 'PUT,DELETE';
add_header 'Access-Control-Allow-Headers' 'Test-CORS, Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Content-Length' 0;
return 204;
}
#Access-Control-Allow-Origin是必须的,其他可选
add_header 'Access-Control-Allow-Origin' 'http://127.0.0.1:8080';
add_header 'Access-Control-Allow-Credentials' 'true';
proxy_pass http://127.0.0.1:80801;
proxy_set_header Host $host;
}
}
}
4、Websocket
webSocket本身不存在跨域问题,所以我们可以利用webSocket来进行非同源之间的通信。
5、浏览器允许跨域
我们使用命令行输入下面指令打开Chrome,此时浏览器会提醒:您使用的是不受支持的命令行标记:--disable-web-security。稳定性和安全性会有所下降。
open -n "/Applications/Google Chrome.app" --args --disable-web-security --user-data-dir=yourdir
我们同样的在简书主页请求百度,却可以正常返回了,不会报跨域错误。
参考链接
https://developer.aliyun.com/article/841748
https://juejin.cn/post/6844903991558537223