spring security 如何解决跨域的

一、什么是 CORS

CORS(Cross-Origin Resource Sharing) 是由 W3C制定的一种跨域资源共享技术标准,其目就是为了解决前端的跨域请求。在JavaEE 开发中,最常见的前端跨域请求解决方案是早期的JSONP,但是JSONP 只支持 GET 请求,这是一个很大的缺陷,而 CORS 则支特多种HTTTP请求方法,也是目前主流的跨域解决方案。

CORS中新增了一组HTTP 请求头字段、通过这些字段,服务器告诉浏览器,那些网站通过浏览器有权限访问哪些资源。同时规定,对那些可能修改服务器数据的HTTP请求方法 (如GET以外的HTTTP 请求等),浏览器必须首先使用OPTIONS 方法发起一个预检请求(prenightst),预检请求的目的是查看服务端是否支持即将发起的跨域请求,如果服务端允许,才包实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证 (如 Cookies、HTTP 认证信息等)。

这是浏览器的一种同源策略安全考虑,如果非同源请求,需要有一个options的预检请求,当预检通过后才能访问其他资源;后端不会有这个问题,当前页面部署的服务器和向后端发起请求如果非同源,那么就会有跨域的问题

关键字:同源 代表 协议 + ip + 端口 一致

二、跨域现象演示

我们需要新建一个工程,启动本地的 localhost:8081 然后再启动一个工程端口在 8082在8082的工程中的 html页面发起一个ajax请求

2.1 后端代码

步骤:创建一个基本的springweb工程,新建一个普通的controller

2.1.1 pom.xml 文件

XML 复制代码
<dependencies>
        <!-- web 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- thymeleaf 模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

    </dependencies>

2.1.2 新建controller

java 复制代码
@Controller
public class HomeController {



    @RequestMapping("/")
    public String index() {
        return "index";
    }


    @RequestMapping("/testHello")
    @ResponseBody
    public String testHello() {
        System.out.println("test Hello");
        return "test Hello ";
    }

}

2.1.3 新建index.html

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script th:src="@{https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js}" type="text/javascript"></script>
</head>
<body>


   <h2> spring cors web</h2>


<script>
    axios.get('/testHello')

</script>

</body>
</html>

2.1.4 当本机访问后查看结果

2.1.5 我们用HBuilder 打开这个文件

当我们用hbuilder打开后,用浏览器打开,这个时候我们会新开一个端口,然后我们用axios 里面使用get请求 testHello接口,会发现控制台有跨域的错误提示

axios.get('http://localhost:8082/testHello').then(response => {

console.log(response.data);

})

.catch(error => {

console.error(error);

});


三、spring web 处理策略

在spring web中已经定义了三种可以处理跨域请求方案

3.1 @CrossOrigin 注解

该注解可以放到类和方法上,当放到类上面时,这个类下面所有的方法都生效

里面属性解释:

alowCredentials: 浏览器是否应当发送凭证信息,如 Cookie.

allowedFeaders:请求被允许的请求头宇段, * 表示所有宇段。

exposedHeaders:哪些响应头可以作为响应的一部分暴露出来注意,这里只可以一一列举,通配符 * 在这里是无效的。

maxAge:预检请求的有效期,有效期内不必再次发送预检请求,默认是1888秒。methods:允许的请求方法,* 表示允许所有方法。

origins:允许的域,*表示允许所有域。

3.1.1 当我们加上后再来测试

java 复制代码
 @CrossOrigin(origins = {"*"})
    @RequestMapping("/testHello")
    @ResponseBody
    public String testHello() {
        System.out.println("test Hello");
        return "test Hello ";
}

我们看到这个时候可以正常输出了

3.2 使用WebMvcConfigurer 重写addCorsMappings配置

java 复制代码
@Configuration
public class WebConfiguration implements WebMvcConfigurer {


    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//处理所有请求
                .allowCredentials(false)
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowedHeaders("*")
                .exposedHeaders("*")
                .maxAge(3600);

    }
}

3.2.1 测试结果

我们注释掉方法上面的//@CrossOrigin(origins = {"*"})

3.3 使用CrosFilter

Cosr Filter 是Spring Web 中提供的一个处理跨域的过滤器,开发者也可以通过该过该过滤器处理跨域。

java 复制代码
@Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
        CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowCredentials(false);
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", configuration);

        registrationBean.setFilter(new CorsFilter(urlBasedCorsConfigurationSource));
        registrationBean.setOrder(-1);

        return registrationBean;

    }

测试同样可以达到效果


四、spring security 处理方案

当我们为项目添加了Spring Security 依赖之后,发现上面三种跨域方式有的失效了,有则可以继续使用,这是怎么回事?

通过@CrossOrigin 注解或者重写 addCorsMappings 方法配置跨域,统统失效了,通CorsFilter配置的跨域,有没有失效则要看过滤器的优先级,如果过滤器优先级高于SpSecurity 过滤器,即先于Spring Security 过滤器执行,则CorsFiter 所配置的跨域处理依然有效;如果过滤器优先级低于Spring Security 过滤器,则CorsFilter 所配置的跨域处理就会失效。

4.1 导入security 依赖

XML 复制代码
<!-- SpringSecurity依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

我们看到又跨域请求又出现了,我们该怎么解决呢?

4.2 过滤器顺序

为了理清楚这个问题,我们先简略了解一下 Filter、DispatchserServlet 以及Interceptor执行顺序。

我们再来看跨域请求过程。由于非简单请求都要首先发送一个预检请求request),而预检请求并不会携带认证信息,所以预检请求就有被 Spring Security 拦截的可能。因此通过@CrossOrigin 注解或者重写 addCorsMappings 方法配置跨域就会失效。如果使用 CorsFilter 配置的跨域,只要过滤器优先级高于 SpringSecurity 过滤器就不会有问题。反之同样会出现问题。

4.3 解决方案

Spring security 为我们提供了更优秀的解决方案

java 复制代码
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .cors()
                .configurationSource(configurationSource()) //处理跨域请求
                .and()
                .csrf().disable();
    }


    /**
     *  配置spring security 跨域解决方案
     * @return
     */
    public CorsConfigurationSource configurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(false);
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

4.3.1 查看效果

我们看到没有报跨域的错误了,但是有一个302 重定向的错误,是因为这台浏览器没有登录,重定向登录页面了;

相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
難釋懷3 小时前
安装Canal
java
是苏浙4 小时前
JDK17新增特性
java·开发语言
不光头强4 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp7 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多7 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood7 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员7 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai