熟悉 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. Hologres揭秘:优化COPY,批量导入性能提升5倍+

    简介: 揭秘Hologres优化COPY的技术原理,实现批量导入性能提升5倍+ Hologres(中文名交互式分析)是阿里云自研的一站式实时数仓,这个云原生系统融合了实时服务和分析大数据的场景,全面兼 ...

  2. [FAQ] 适用于 macOS / Arm64 (M1/M2) 的 VisualBox

      使用与 Windows.Linux.macOS 的x86架构的一般在下面地址中下载: Download VisualBox:https://www.virtualbox.org/wiki/Down ...

  3. [FAQ] Solidity 实现倒计时 (count down) ?

    思路:一种方式是使用 ethereum-alarm-clock,另一种是合约实现当前过去了多少时间,外部进行不间断调用获得. Any else? Refer:Solidity能倒计时吗 Link:ht ...

  4. WPF 已知问题 清空 CollectionView 的 SortDescriptions 可能抛出空异常

    本文记录一个 WPF 的已知问题,在通过 CollectionViewSource 获取到 CollectionView 之后,如果 CollectionViewSource 对象已被 GC 回收,将 ...

  5. 16.prometheus监控总结

    一.监控流程总结 1.需要在被监控的服务器上安装xx_exporter来收集数据(可以是源码安装,最好用docker.docker-compose) 2.添加Prometheus配置,去收集(xx_e ...

  6. K8s控制器---DaemonSet控制器(12)

    一.DaemonSet控制器:概念.原理解读 1.1 DaemonSet概述 DaemonSet 控制器能够确保 k8s 集群所有的节点都运行一个相同的 pod 副本,当向 k8s 集群中增加 nod ...

  7. vue首次缓存判断

    在向本地缓存存值前,判断localstory是否已经存在某个属性,没有就正常存,有就替换掉 created(){ if(localStorage.getItem("属性名") != ...

  8. 学会使用 NumPy:基础、随机、ufunc 和练习测试

    NumPy NumPy 是一个用于处理数组的 Python 库.它代表"Numerical Python". 基本 随机 ufunc 通过测验测试学习 检验您对 NumPy 的掌握 ...

  9. 入门Semantic Kernel:OneApi集成与HelloWorld

    引言 从这一章节开始正式进入我们的 Semantic Kernel 的学习之旅了. 什么是Semantic Kernel? Semantic Kernel是一个轻量级的开源框架,通过 Semantic ...

  10. Nginx教程+笔记

    Nginx 学习视频: 2020最新 Nginx教程全面讲解(Nginx快速上手) https://www.bilibili.com/video/BV1W54y1z7GM?t=553&p=14 ...