熟悉 web 系统开发的同学,对下面这样的错误应该不会太陌生。

之所以会出现这个错误,是因为浏览器出于安全的考虑,采用同源策略的控制,防止当前站点恶意攻击 web 服务器盗取数据。

01、什么是跨域请求

同源策略,简单的说就是当浏览器访问 web 服务器资源时,只有源相同才能正常进行通信,即协议、域名、端口号都完全一致,否则就属于跨域请求。当发起跨域请求时,服务端是能收到请求并正常返回结果的,只是结果被浏览器拦截了。

像上文中,浏览器访问的站点是http://127.0.0.1:8848/,而站点内发起的接口请求源是http://localhost:8080,因为不同源,所以报跨域请求异常。

由此可见,想要实现接口请求的正常访问,浏览器的访问站点源和接口请求源,必须得一致。

事实上,在现在流行的前后端分离的开发模式下,很难做到请求源高度一致,那怎么办呢?

答案肯定是有办法啦!

虽然浏览器出于安全的考虑,默认采用同源策略控制,以便减少服务器被恶意攻击的机会,但是开发者可以通过CORS协议在浏览器内实现站内跨域请求访问。

实现很简单,通过在 web 服务器中增加一个特殊的Header响应属性来告诉浏览器解除跨域的限制,如果浏览器支持CORS并且判断允许通过的话,此时发起的跨域请求就可以正常展示了。

常用的 Header 响应属性如下:

Header 属性 作用
Access-Control-Allow-Origin 设置允许跨域请求的请求源,比如www.xxx.com
Access-Control-Max-Age 设置预检请求的结果能被缓存的时间,单位秒,比如1800
Access-Control-Allow-Methods 设置允许跨域请求的方法,比如GET, POST, OPTIONS, PUT, DELETE
Access-Control-Allow-Headers 设置允许跨域请求的头部信息,比如Content-Type, Accept
Access-Control-Allow-Credentials 设置是否允许携带凭证(比如cookies),参数值只能是true或者不设置

带着以上的信息,我们就一起来了解一下如何在 Spring Boot 应用中实现跨域访问。

02、解决方案

2.1、方法一:采用过滤器的方式全局配置

采用过滤器的方式来实现所有接口支持跨域请求,是一种比较通用的做法,也是 Java web 项目中常用的方法,实现过程如下!

首先,创建一个实现自Filter接口的过滤器,示例如下:

public class CrossFilter implements Filter {

    /**
* 允许跨域的白名单域名
*/
private final static Set<String> ALLOW_DOMAINS = new HashSet<>(); static {
ALLOW_DOMAINS.add("http://127.0.0.1:8848");
} @Override
public void init(FilterConfig config) throws ServletException {} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 获取客户端原始请求域
String origin = request.getHeader("Origin");
String originDomain = removeHttp(origin);
if(ALLOW_DOMAINS.contains(originDomain)){
// 在响应对象中,添加CROS协议相关的header属性
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,HEAD,PUT,PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization,authorization");
response.setHeader("Access-Control-Allow-Credentials","true");
}
//继续往下传递
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() {} /**
* 移除http协议头部
* @param url
* @return
*/
public static String removeHttp(String url){
return url.replace("http://", "").replace("https://", "");
} }

接着,将其注册到Servlet容器中,示例如下:

@Configuration
public class FilterConfig { /**
* 添加CrossFilter过滤器
* @return
*/
@Bean
public FilterRegistrationBean crossFilterBean() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setName("crossFilter"); // 指定过滤器名称
registration.setFilter(new CrossFilter()); // 指定过滤器实现类
registration.setUrlPatterns(Collections.singleton("/*"));// 指定拦截路径
registration.setOrder(1);// 指定顺序
return registration;
}
}

最后,启动服务后,再到浏览器中发起跨域请求,看看效果如下。

从结果上看,浏览器成功进行了跨域请求,并展示了服务器返回的结果。

2.2、方法二:通过全局配置类实现跨域访问

在 Spring Boot 应用,除了采用过滤器的方式实现跨域访问外,我们还可以通过全局配置类实现跨域访问。

实现方法也非常简单,只需要重写WebMvcConfigurer接口中的addCorsMappings方法即可,示例如下:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.maxAge(3600)
.allowedHeaders("Origin", "Accept", "Content-Type", "Authorization")
.allowCredentials(true);
}
}

其中allowedOrigins("*")表示对所有请求都允许跨域访问。

2.3、方法三:通过CrossOrigin注解实现跨域访问

某些场景,如果不希望所有的接口都能跨域访问,只想在部分接口上放开跨域访问。此时,可以通过 Spring Boot 提供的@CrossOrigin注解,在对应的方法上加上该注解,即可实现跨域访问。

示例如下:

@RestController
public class UserController { @Autowired
private UserService userService; @CrossOrigin
@PostMapping(value = "/queryAll")
public List<User> queryAll(){
List<User> result = userService.queryAll();
return result;
}
}

如果使用在controller类上,表示当前类下的所有接口方法都允许跨域访问。

同时,@CrossOrigin注解也支持设置更小的粒度,示例如下:

@CrossOrigin(origins = "http://domain.com", maxAge = 1800)

更多的属性行为,内容如下:

  • origins: 允许的源列表,多个源可以使用逗号分隔
  • methods: 允许的 HTTP 方法列表
  • allowedHeaders: 允许的请求头列表,默认情况下,允许所有请求头
  • allowCredentials:设置是否允许携带凭证
  • maxAge: 预检请求的缓存时间(以秒为单位)

03、小结

最后总结一下,在 Spring Boot 服务中可以通过过滤器或者配置类实现全局跨域访问,也可以通过@CrossOrigin注解实现局部跨域访问。

跨域访问的配置,更适合在开发环境中方便前后端进行联调对接。为了安全起见,在上生产的时候,建议将其关闭掉或者做限制。

此外,想要获取项目源代码的小伙伴,可以点击:跨域请求,即可获取取项目的源代码。

04、参考

1.https://cloud.tencent.com/developer/article/1655583

2.https://cloud.tencent.com/developer/article/1924258

写到最后

不会有人刷到这里还想白嫖吧?点赞对我真的非常重要!在线求赞。加个关注我会非常感激!

本文已整理到技术笔记中,此外,笔记内容还涵盖 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多线程、JPA、MyBatis、MySQL、微服务等技术栈。

需要的小伙伴可以点击 技术笔记 获取!

盘点 Spring Boot 解决跨域请求的几种办法的更多相关文章

  1. Spring Boot + Vue 跨域请求问题

    使用Spring Boot + Vue 做前后端分离项目搭建,实现登录时,出现跨域请求 Access to XMLHttpRequest at 'http://localhost/open/login ...

  2. spring boot 解决跨域访问

    package com.newings.disaster.shelters.configuration; import org.springframework.context.annotation.B ...

  3. spring mvc \ spring boot 允许跨域请求 配置类

    用@Component 注释下,随便放个地方就可以了 package com.chinaws.wsarchivesserver.core.config; import org.springframew ...

  4. Spring boot 解决跨域问题

    import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.we ...

  5. spring boot 解决 跨域 的两种方法 -- 前后端分离

    1.前言 以前做项目 ,基本上是使用 MVC 模式 ,使得视图与模型绑定 ,前后端地址与端口都一样 , 但是现在有些需求 ,需要暴露给外网访问 ,那么这就出现了个跨域问题 ,与同源原则冲突, 造成访问 ...

  6. spring boot处理跨域请求代码

    @Configuration @WebFilter(filterName = "CorsFilte") public class CorsFilter implements Fil ...

  7. Spring Boot 允许跨域设置失败的问题深究

    在公司开发过程中,一个前后端分离的项目遇见了跨域的问题. 前端控制台报错:No 'Access-Control-Allow-Origin' header is present on the reque ...

  8. spring @CrossOrigin解决跨域问题

    阅读目录: 一.跨域(CORS)支持: 二.使用方法: 1.controller配置CORS 2.全局CORS配置 3.XML命名空间 4.How does it work? 5.基于过滤器的CORS ...

  9. js中ajax如何解决跨域请求

    js中ajax如何解决跨域请求,在讲这个问题之前先解释几个名词 1.跨域请求 所有的浏览器都是同源策略,这个策略能保证页面脚本资源和cookie安全 ,浏览器隔离了来自不同源的请求,防上跨域不安全的操 ...

  10. Ajax 调用webservice 解决跨域请求和发布到服务器后本地调用成功外网失败的问题

        webservice 代码 /// <summary> /// MESService 的摘要说明 /// </summary> [WebService(Namespac ...

随机推荐

  1. 解决 Serverless 落地困难的关键,是给开发者足够的“安全感”

    ​简介:越来越多的云产品都会向全托管.Serverless 形态演进.当云的产品体系 Serverless 化达到一个临界值,通过函数计算这样的 Serverless 计算服务结合其他 Serverl ...

  2. AI圈内卷?天池团聚请来专家集体“问诊”

    ​简介: 近期杭州云栖大会上出现了一个"数据博物馆",最吸引眼球的"展品",竟是行业大规模开源数据集.不仅数量多达上百个,还覆盖零售.文娱.工业.医疗.自然科学 ...

  3. Serverless Devs 2.0 全新发布,让 Serverless 应用开发更简单

    ​简介: 2020 年 10 月 23日,阿里巴巴正式宣布开源其首个 Serverless 开发者平台 Serverless Devs.历经近一年精心打磨,今天 Serverless Devs 2.0 ...

  4. 简说Python之面向对象

    目录 Python对象 对象的内置方法 __init__方法 __str__方法 对象的多态 对象的封装 对象的继承 之前介绍了函数.对象代表了什么.可以称为"一篮子"计划,把数据 ...

  5. @Async异步失效的9种场景

    前言 最近星球中有位小伙伴问了我一个问题:他在项目某个方法使用@Async注解,但是还是该方法还是同步执行了,异步不起作用,到底是什么原因呢? 伪代码如下: @Slf4j @Service publi ...

  6. go 操作elasticsearch

    olivere/elastic 包 github.com/olivere/elastic doc: https://pkg.go.dev/github.com/olivere/elastic?utm_ ...

  7. java 读取气象专业格式NetCDF文件

    一.NetCDF简介NetCDF全称为network Common Data Format( "网络通用数据格式"),是一个软件库与机器无关的数据格式,支持创建,访问基于数组的科研 ...

  8. mybatisplus 中查询的实体对应的表名是动态的解决方案

    开发中遇到需要查询一些表里的数据,这些数据按照一定的规则存放在不同的数据库表里,例如表名是table_name+月份  table_name_2024_05,table_name_2024_04这样, ...

  9. 腾讯云服务器sdk

    参考:https://cloud.tencent.com/document/product/494/7244 pip install -i https://mirrors.tencent.com/py ...

  10. VSCode:Mac(M1)配置VSCode中C/C++环境(只需三步)

    只需三步. 1. 下载CodeLLDB插件: 2. 配置tasks.json { "version": "2.0.0", "tasks": ...