SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

kenx
• 阅读 2067

前言

最近在做项目的时候,基于前后端分离的权限管理系统,后台使用 Spring Security 作为权限控制管理, 然后在前端接口访问时候涉及到跨域,但我怎么配置跨域也没有生效,这里有一个坑,在使用Spring Security时候单独配置,SpringBoot 跨越还不行,还需要配置Security 跨域才行。

什么是跨域

跨域是一种浏览器同源安全策略,即浏览器单方面限制脚本的跨域访问

在 HTML 中,<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为若协议 + 域名 + 端口号均相同,那么就是同域

当前页面URL和请求的URL首部(端口之前部分)不同则会造成跨域。通俗点讲就是两个URL地址,端口之前部分,只要有一点不同即发生跨域。

例如:

1. 在http://a.baidu.com下访问https://a.baidu.com资源会形成协议跨域。

2. 在a.baidu.com下访问b.baidu.com资源会形成主机跨域。

3. 在a.baidu.com:80下访问a.baidu.com:8080资源会形成端口跨域。

解决跨域的常见方式

JSONP

由于浏览器允许一些带有src属性的标签跨域,常见的有iframe、script、img等,所以JSONP利用script标签可以实习跨域

前端通过script标签请求,并在callback中指定返回的包装实体名称为jsonp(可以自定义)

<script src="http://aaa.com/getusers?callback=jsonp"></script>

后端将返回结果包装成所需数据格式

jsonp({
    "error":200,
    "message":"请求成功",
    "data":[{
        "username":"张三",
        "age":20
    }]
})

总结:JSONP实现起来很简单,但是只支持GET请求跨域,存在较大的局限性

CORS

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

规范中有一组新增的HTTP首部字段 它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求

注意:CORS不支持IE8以下版本的浏览器

| CORS Header属性 | 解释 |
| :--------- | :--: | -----------: | |Access-Control-Allow-Origin |允许http://www.xxx.com域(自行设置,这里只做示例)发起跨域请求 |Access-Control-Max-Age |设置在86400秒不需要再发送预校验请求 |Access-Control-Allow-Methods |设置允许跨域请求的方法 |Access-Control-Allow-Headers |允许跨域请求包含content-type |Access-Control-Allow-Credentials| 设置允许Cookie

SpringBoot解决跨越方式

@CrossOrigin 注解

这种是SpringBoot自带的注解,使用非常简单,只需要在对应的接口添加上次注解就行

就表示这个接口支持跨域,其中origins = "*" 表示所有的地址都可以访问这个接口,也可以写具体的地址,表示只有这个地址访问才能访问到接口

SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

可以注解在类上,和方法上,类上表示此controller所有接口都支持跨域,单个方法上表示只有这个接口支持跨域

这种方法虽然优雅简单,但是缺点也不小,需要跨域的接口都需要加上这个注解,这对前后端分离的项目是不友好的,需要添加很多次,所以这种方式基本上用的很少

拦截器方式

重写WebMvcConfigurer的addCorsMappings 方法

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//项目中的所有接口都支持跨域
                .allowedOriginPatterns("*")//所有地址都可以访问,也可以配置具体地址
                .allowCredentials(true)
                .allowedMethods("*")//"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"
                .maxAge(3600);// 跨域允许时间
    }
}

注意allowedOrigins("*")allowCredentials(true)true时候会出现错误需要改成allowedOriginPatterns("*")或者单独指定接口allowedOrigins("http//www.baidu.com")

@Configuration表示是配置类,在项目启动的时候会加载。实现WebMvcConfigurer接口并重写addCorsMappings 方法。代码比较简单,也有注释

Filter过滤器方式

  1. 方式一

    @Bean
     public CorsFilter corsFilter() {
          //1.添加CORS配置信息
         CorsConfiguration config = new CorsConfiguration();
           //放行哪些原始域
           config.addAllowedOrigin("*");
           //是否发送Cookie信息
           config.setAllowCredentials(true);
           //放行哪些原始域(请求方式)
           config.addAllowedMethod("*");
           //放行哪些原始域(头部信息)
           config.addAllowedHeader("*");
           //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
           config.addExposedHeader("*");
    
         //2.添加映射路径
         UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
         configSource.registerCorsConfiguration("/**", config);
    
         //3.返回新的CorsFilter.
         return new CorsFilter(configSource);
     }
  2. 方式二 基于servler 最原始的配置方式

@Slf4j
@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, client_id, uuid, Authorization");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        filterChain.doFilter(servletRequest,response);
    }
}

以上三种方法都可以解决问题,最常用的应该是第一种、第二种,控制在自家几个域名范围下足以,一般没必要搞得太细。

这三种配置方式都用了的话,谁生效呢,类似css中样式,就近原则,懂了吧

SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

Spring Security启用CORS支持

如果项目中使用了Spring Security 配置了上面跨越方式还不行,需要单独指定Spring Security跨域

否则跨越不会生效

Spring Security对CORS提供了非常好的支持,只需在配置器中启用CORS支持,并编写一 个CORS配置源即可

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // We don't need CSRF for this example
        http.cors().and().csrf().disable()
                // dont authenticate this particular request
                .authorizeRequests().antMatchers("/", "/*.html", "/favicon.ico", "/css/**", "/js/**", "/fonts/**", "/layui/**", "/img/**",
                "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/pages/**", "/druid/**",
                "/statics/**", "/login", "/register").permitAll().
                // all other requests need to be authenticated
                        anyRequest().authenticated().and().
                // make sure we use stateless session; session won't be used to
                // store user's state.
                //覆盖默认登录
                        exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
                // 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        // Add a filter to validate the tokens with every request
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
点赞
收藏
评论区
推荐文章
待兔 待兔
2年前
什么是跨域以及如何解决?通俗易懂带你彻底搞定
什么是跨域以及如何解决?通俗易懂带你彻底搞定现在的web项目,很多都是前后端分离,特别容易出现跨域问题那么什么是跨域问题呢?本篇文章带你彻底从本质上弄明白什么是跨域问题以及如何解决一跨域有什么现象?我们先看一下
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Nginx解决跨域问题(CORS)
前言CORS(CrossOriginResourceSharing)跨域资源共享,是一种允许当前域(domain)的资源(比如html/js/webservice)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(thesameoriginsecuritypolicy)浏览器会禁止这种跨域请求。 如:a.c
Stella981 Stella981
3年前
No 'Access
跨域说明前后端分离下,跨域已是一个老生常谈的话题,但很多小伙伴还是经常面临这样的问题,且解决方案多变多样。这里介绍一种简单直接的后端解决方案。解决跨域(服务端)/CorsConfig跨域@authorylyue@since2018年11
Stella981 Stella981
3年前
Angular 应用解决跨域访问的问题
在前后台分离的应用中,Angular与Java是一对好搭档。但是如果是分开部署应用,则势必会遇到跨域访问的问题。什么是跨域启动应用之后,有些浏览器会提示如下告警信息:No'AccessControlAllowOrigin'headerispresentontherequestedresource.
Stella981 Stella981
3年前
SpringBoot实现jsonp跨域通信
实现jsonp跨域通信实现基于jsonp的跨域通信方案原理浏览器对非同源ajax请求有限制,不允许发送跨域请求目前跨域解决方案有两种cros配置jsonp请求cros为新规范,通过一个head请求询问服务器是否允许跨域,若不允许则被拦截jso
Easter79 Easter79
3年前
SpringBoot实现jsonp跨域通信
实现jsonp跨域通信实现基于jsonp的跨域通信方案原理浏览器对非同源ajax请求有限制,不允许发送跨域请求目前跨域解决方案有两种cros配置jsonp请求cros为新规范,通过一个head请求询问服务器是否允许跨域,若不允许则被拦截jso
Stella981 Stella981
3年前
Spring Boot 中三种跨域场景总结
@\toc\跨域这个问题松哥之前写过文章,但是最近收到小伙伴们的一些问题,让我发现之前的总结不够全面,因此打算再写一篇文章,来和大家分享一下SpringBoot中的跨域问题。这次我把SpringBoot中的跨域问题分为了三个场景:普通跨域SpringSecurity跨域OAuth2跨域分
liam liam
1年前
解决 Axios 跨域阻碍,提高前端接口访问效率
跨域是指访问另外一个域的资源,由于浏览器的同源策略,默认情况下使用XMLHttpRequest和Fetch请求时是不允许跨域的。跨域的根本原因是浏览器的同源策略,这是由浏览器对施加的安全限制。Axios跨域常见报错跨域请求被阻止(CrossOriginRe
京东云开发者 京东云开发者
6个月前
深入跨域 - 解决方案
1前言前文《深入跨域从初识到入门》中,大家已经对同源与跨域的产生历史与重要性等有了一个初步的了解了,那么我们应该如何解决在日常开发中遇到的跨域引起的问题呢?2一览图我们将日常开发中的跨域解决方案大体分为两类:iframe跨域与API跨域:3iframe跨域