盘点 Spring Boot 中解决跨域访问的几种方式

01、故事背景

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

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

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

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

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

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

答案肯定是有办法啦!

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

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

常用的 Header 响应属性如下:


带着以上的信息,我们就一起来了解一下如何在 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注解实现局部跨域访问。

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

最后,送大家一份Java 生态知识体系/面试必看资料还在找工作的小伙伴看过来啦!好多读者都问我有没有面试题神器,我苦苦整了一份内部资料,Java 生态知识体系/面试必看资料!
据说已经有小伙伴通过这套资料,成功的入职了蚂蚁金服、字节跳动等大厂
而且,这些资料不是扫描版的,里面的文字都可以直接复制,非常便于我们学习:




如果你想获得完整PDF可以通过以下方式获得

面试大全怎么获取:
1.加我微信备注【Java极客技术 2.后台回复【pdf即可。

喜欢就分享
认同就点赞

支持就在看

一键四连,你的offer也四连

相关推荐

  • 长上下文能力只是吹牛?最强GPT-4o正确率仅55.8%,开源模型不如瞎蒙
  • Llama 3.1要来啦?!测试性能战胜GPT-4o
  • 刚刚,中国IMO奥数憾失第一,五连冠统治被美国队终结
  • [开源]真正意义上零侵入接口文档生成工具,无需增加一行配置代码
  • 成本降低10万倍!生成一周大气模拟仅需9.2秒,谷歌气候模型登Nature
  • 视频生成大战2.0!大厂狂卷底层模型,创企5个月吸金44亿
  • 面试官:加密后的数据如何进行模糊查询?
  • Spring Boot集成Spire.doc实现对word的操作
  • 不会?到底上OPC UA还是MODBUS???
  • 2K Star牛牛牛!!!全球频道,一键直达,探索IPTV新天地
  • 损失函数(Loss Function)
  • 2个月暴增10k star,新一代高颜值、现代化的 Git 可视化工具
  • 最有用的25个 Matplotlib图(含Python代码模板)
  • 吴恩达团队新作!
  • Python 面试时千万别这样命名函数,因为这个被淘汰可太不值了
  • 阿里这款多级缓存框架一定要掌握,非常不错!
  • AI替代职业,最高和最低Top25
  • 一文带你了解 Chrome AI
  • 苹果开源7B大模型,训练过程数据集一口气全给了,网友:开放得不像苹果
  • 当小说续写结合AI RPG,AI陪伴的新玩法现状如何?|专访彩云小梦