你们好,我是金金金。
前置知识
本篇文章以通俗易懂的方式进行描述,自己组织语言进行输出,尽量让每一个人都能看得懂。哪里有说的不正确的地方 大佬请在评论区指正!
- 首先需要了解浏览器的同源策略
浏览器的同源策略
MDN
解释地址:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
简单来说,同源策略是浏览器的一项重要安全机制,它限制了从一个来源加载的脚本如何与来自另一个来源的资源进行交互。其目的是防止不同来源之间的恶意行为,例如跨站脚本攻击(XSS)和跨站请求伪造(CSRF)
怎么算同源?
两个
URL
的 协议、域名 和 端口 必须完全相同,才被认为是同源。否则,它们被认为是不同源的,那么就会发生跨域。如下
什么是跨域?
了解了浏览器的同源策略之后,想必你心中已经有答案了吧,两个
URL
的 协议、域名 和 端口 有一个不同那么就会发生跨域,控制台报错如下
如何解决跨域?
- 这里我分为前端后端分别讲解具体的解决方案。
前端
vite代理
- 例如
vite
和webpack
都有配置代理能解决跨域的一个方案。这里只讲解vite
的
vite.config.ts
配置文件:通过server.proxy
选项来配置代理规则,我这里是ts
项目,项目下一般都会有一个vite.config.ts
,不是ts
的项目那么就是.js
文件,都差不多,配置如下:图中写明了各个参数意思
- 然后在你封装请求的文件当中使用这个
/api
作为接口基准url
即可
需要注意:
Vite
的代理功能仅限于开发阶段,不会对生产环境产生任何影响。在生产环境中,你需要通过其他方式来处理跨域问题,比如在后端服务器上启用CORS
,或者在反向代理服务器(如Nginx
)上进行配置。
JSONP
原理
利用
<script>
标签的src
属性没有跨域限制的特性来实现跨域数据访问
怎么做呢
事先定义一个用于获取跨域响应数据的回调函数,并通过没有同源策略限制的
script
标签发起一个请求(将回调函数的名称放到这个请求的query
参数里),然后服务端返回这个回调函数的执行,并将需要响应的数据放到回调函数的参数里,前端的script
标签请求到这个执行的回调函数后会立马执行,于是就拿到了执行的响应数据。
- 参考代码如下:
function handleResponse(response) {
//处理服务器返回的数据
}
var script = document.createElement('script');
script.src = 'https://jsonp.com/data?callback=handleResponse';
document.body.appendChild(script);
注意,
JSONP
只支持GET
请求,因为它依赖于<script>
标签的跨域特性,而<script>
标签不支持POST
等其他HTTP
方法
无需过多研究,了解即可,因为现在基本不会用到这种方式,感兴趣的小伙伴们自行百度~
选哪种?
如果你们项目开发阶段联调跨域,后端让前端解决的话 优先选择第一种:代理方式即可!
服务端
- 这里以
Java
为例子
返回新的CorsFilter
@Configuration
public class GlobalCorsConfig {
/**
* 配置跨域过滤器
* 允许特定条件下的跨域请求,以适应前端开发需求
*
* @return CorsFilter 配置好的跨域过滤器实例
*/
@Bean
public CorsFilter corsFilter() {
// 创建一个新的CORS配置实例
CorsConfiguration config = new CorsConfiguration();
// 允许所有域名进行跨域调用
config.addAllowedOrigin("*");
// 允许跨越发送Cookie
config.setAllowCredentials(true);
// 允许所有头部信息进行跨域调用
config.addAllowedHeader("*");
// 允许所有请求方法进行跨域调用
config.addAllowedMethod("*");
// 创建一个URL基于CORS配置的源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 将CORS配置应用到所有路径
source.registerCorsConfiguration("/**", config);
// 返回基于所配置的源创建的CORS过滤器实例
return new CorsFilter(source);
}
}
全局跨域
注意:需要确保
Spring
能扫描到这个配置类。
重写WebMvcConfigurer
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //配置了所有的路由都可以跨域请求
.allowedHeaders("*") //配置了允许发送的自定义请求头
.allowedMethods("*") //配置了路径下所有的请求都可以跨域请求
.allowedOriginPatterns("*") //解决跨域资源共享(CORS)问题的一个配置项,用于允许来自任何来源的跨域请求
.allowCredentials(true) //指定在跨域请求中是否允许浏览器发送包含凭证信息的请求
.maxAge(3600); //指定在给定的时间范围内,是否允许浏览器缓存特定资源的请求结果。
}
}
全局跨域
注意:需要确保
Spring
能扫描到这个配置类。
使用CrossOrigin注解
针对某个控制器方法配置跨域
@RestController public class MyController {
@CrossOrigin(origins = "http://example.com") // 允许来自指定域的跨域请求,"*"代表全部
@GetMapping("/api/data")
public String getData() {
return "This is cross-origin data";
}
}
针对整个控制器配置跨域
@RestController
@CrossOrigin(origins = "*")
public class TestController {
@RequestMapping("/test")
public String test() {
return "This is cross-origin data";
}
}
局部跨域
无论是针对某个控制器方法还是针对整个控制器配置,都是属于局部配置,不建议使用,繁琐。
手动设置响应头
@RestController
public class TestController {
@RequestMapping("/test")
public HashMap<String, Object> test(HttpServletResponse response) {
// 设置跨域
response.setHeader("Access-Control-Allow-Origin", "*");
return new HashMap<String, Object>() {{
put("state", 200);
put("data", "success");
put("msg", "");
}};
}
局部跨域
纯手动添加响应头,不建议使用。繁琐。每个方法都得写
实现ResponseBodyAdvice
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
/**
* 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
*
* @param methodParameter 方法参数,包含了方法的信息以及参数的信息
* @param aClass 返回值的类型
* @return 返回ture表示重写
*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
/**
* 方法返回之前调用此方法
*
* @param o 方法返回值
* @param methodParameter 方法参数,包含了方法的信息以及参数的信息
* @param mediaType 返回值的类型
* @param aClass 返回值的类型
* @param serverHttpRequest 请求
* @param serverHttpResponse 响应
* @return
*/
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
// 设置跨域
serverHttpResponse.getHeaders().set("Access-Control-Allow-Origin", "*");
return o;
}
}
全局跨域
注意:需要确保
Spring
能扫描到这个配置类。
Nginx
解决跨域
- 在
Nginx
服务器的配置文件中添加以下代码
server {
listen 80;
server_name your_domain.com;
location /api {
# 允许跨域请求的域名,* 表示允许所有域名访问
add_header 'Access-Control-Allow-Origin' '*';
# 允许跨域请求的方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# 允许跨域请求的自定义 Header
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
# 允许跨域请求的 Credential
add_header 'Access-Control-Allow-Credentials' 'true';
# 预检请求的存活时间,即 Options 请求的响应缓存时间
add_header 'Access-Control-Max-Age' 3600;
# 处理预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
}
# 其他配置...
}
server { listen 80; server_name your_domain.com; }
作用: 定义服务器监听的端口和域名。
解释:listen 80
表示服务器监听 80 端口,这是 HTTP 的默认端口。server_name your_domain.com
表示该服务器处理your_domain.com
的请求。这里的server
块是 Nginx 配置的基础结构,用于定义服务器如何处理特定域名的请求。
结语
耐心看完本篇文章,解决跨域就是简简单单轻轻松松
好啦,本篇文章到此结束,选择适合你们的一种解决方案吧~
- 编写有误还请大佬指正,万分感谢。