经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
盘点 Spring Boot 解决跨域请求的几种方法
来源:cnblogs  作者:潘志的研发笔记  时间:2024/6/17 15:09:02  对本文有异议

熟悉 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接口的过滤器,示例如下:

  1. public class CrossFilter implements Filter {
  2. /**
  3. * 允许跨域的白名单域名
  4. */
  5. private final static Set<String> ALLOW_DOMAINS = new HashSet<>();
  6. static {
  7. ALLOW_DOMAINS.add("http://127.0.0.1:8848");
  8. }
  9. @Override
  10. public void init(FilterConfig config) throws ServletException {}
  11. @Override
  12. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  13. HttpServletRequest request = (HttpServletRequest)servletRequest;
  14. HttpServletResponse response = (HttpServletResponse)servletResponse;
  15. // 获取客户端原始请求域
  16. String origin = request.getHeader("Origin");
  17. String originDomain = removeHttp(origin);
  18. if(ALLOW_DOMAINS.contains(originDomain)){
  19. // 在响应对象中,添加CROS协议相关的header属性
  20. response.setHeader("Access-Control-Allow-Origin", origin);
  21. response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,HEAD,PUT,PATCH");
  22. response.setHeader("Access-Control-Max-Age", "3600");
  23. response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization,authorization");
  24. response.setHeader("Access-Control-Allow-Credentials","true");
  25. }
  26. //继续往下传递
  27. filterChain.doFilter(servletRequest, servletResponse);
  28. }
  29. @Override
  30. public void destroy() {}
  31. /**
  32. * 移除http协议头部
  33. * @param url
  34. * @return
  35. */
  36. public static String removeHttp(String url){
  37. return url.replace("http://", "").replace("https://", "");
  38. }
  39. }

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

  1. @Configuration
  2. public class FilterConfig {
  3. /**
  4. * 添加CrossFilter过滤器
  5. * @return
  6. */
  7. @Bean
  8. public FilterRegistrationBean crossFilterBean() {
  9. FilterRegistrationBean registration = new FilterRegistrationBean();
  10. registration.setName("crossFilter"); // 指定过滤器名称
  11. registration.setFilter(new CrossFilter()); // 指定过滤器实现类
  12. registration.setUrlPatterns(Collections.singleton("/*"));// 指定拦截路径
  13. registration.setOrder(1);// 指定顺序
  14. return registration;
  15. }
  16. }

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

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

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

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

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

  1. @Configuration
  2. public class WebMvcConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addCorsMappings(CorsRegistry registry) {
  5. registry.addMapping("/**")
  6. .allowedOrigins("*")
  7. .allowedMethods("GET", "POST", "PUT", "DELETE")
  8. .maxAge(3600)
  9. .allowedHeaders("Origin", "Accept", "Content-Type", "Authorization")
  10. .allowCredentials(true);
  11. }
  12. }

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

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

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

示例如下:

  1. @RestController
  2. public class UserController {
  3. @Autowired
  4. private UserService userService;
  5. @CrossOrigin
  6. @PostMapping(value = "/queryAll")
  7. public List<User> queryAll(){
  8. List<User> result = userService.queryAll();
  9. return result;
  10. }
  11. }

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

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

  1. @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

原文链接:https://www.cnblogs.com/dxflqm/p/18251873

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号