Spring Security in Action 第十章 SpringSecurity应用CSRF保护和CORS跨域请求

本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringSecurity相关知识相关知识,打造完整的SpringSecurity学习步骤,提升工程化编码能力和思维能力,写出高质量代码。希望大家都能够从中有所收获,也请大家多多支持。
专栏地址:SpringSecurity专栏
本文涉及的代码都已放在gitee上:gitee地址
如果文章知识点有错误的地方,请指正!大家一起学习,一起进步。
专栏汇总:专栏汇总


本章包括

  • 实施跨站请求伪造保护
  • 定制CSRF保护
  • 应用跨源资源共享配置

我们已经了解了过滤器链以及它在Spring Security架构中的作用。我们在第9章中研究了几个例子,在这些例子中我们定制了过滤器链。但Spring Security也会在链上添加自己的过滤器。在本章中,我们将讨论应用CSRF保护的过滤器和与CORS配置相关的过滤器。你将学习如何定制这些过滤器,使之完美地适合你的应用场景。

10.1 在应用程序中应用跨站请求伪造(CSRF)保护

你可能已经注意到,到目前为止的大多数例子中,我们只用HTTP GET实现了我们的api。此外,当我们需要配置HTTP POST时,我们还必须在配置中添加一个补充指令来禁用CSRF保护。之所以不能用HTTP POST直接调用api,是因为CSRF保护,在Spring Security中是默认启用的。

在本节中,我们将讨论CSRF保护以及何时在你的应用程序中使用它。 CSRF是一种广泛的攻击类型,容易受到CSRF攻击的应用程序可以迫使用户在认证后在Web应用程序上执行违规的操作。你不希望你开发的应用程序有CSRF漏洞,允许攻击者欺骗你的用户进行违规的操作。

因为了解如何缓解这些漏洞是至关重要的,所以我们首先回顾一下什么是CSRF以及它是如何工作的。然后我们讨论Spring Security用来缓解CSRF漏洞的CSRF令牌机制。我们继续获得一个令牌并使用它来调用HTTP POST方法的端点。我们用一个使用REST api的程序来证明这一点。一旦你学会了Spring Security如何实现其CSRF令牌机制,我们就会讨论如何在现实世界的应用场景中使用它。最后,你将学习Spring Security中CSRF令牌机制一些定制。

10.1.1 How CSRF protection works in Spring Security

在本节中,我们将讨论Spring Security如何实现CSRF保护。首先了解CSRF保护的基本机制是很重要的。我遇到过很多情况,误解了CSRF保护的工作方式,导致开发者误用它,要么在应该启用的情况下禁用它,要么反其道而行。就像框架中的其他功能一样,你必须正确地使用它才能为你的应用程序带来价值。

作为一个例子,考虑这个场景(图10.1):你在工作中,使用一个网络工具来存储和管理你的文件。通过这个工具,在一个网络界面中,你可以添加新的文件,为你的记录添加新的版本,甚至可以删除它们。你收到一封电子邮件,要求你为一个特定的原因打开一个页面。你打开了该网页,但该网页是空白的,或者它把你重定向到一个已知的网站。你回到你的工作中去,但观察到你所有的文件都不见了

image-20230127150724066

图10.1 在用户登录他们的账户后,他们进入一个包含伪造代码的页面。这段代码冒充用户,可以代表用户执行不需要的操作。

发生了什么事?你被登录到应用程序中,所以你可以管理你的文件。当你添加、更改或删除一个文件时,与你互动的网页从服务器调用一些api来执行这些操作。当你通过点击电子邮件中的未知链接打开外国网页时,该网页调用请求并代表你执行操作(它删除了你的文件)。它能做到这一点是因为你之前登录过,所以服务器相信这些操作来自于你。你可能认为有人不可能如此轻易地欺骗你点击来自外国邮件或信息的链接,但相信我,这发生在很多人身上。大多数网络应用程序用户并不了解安全风险。因此,如果你,知道所有的技巧来保护你的用户,建立安全的应用程序,而不是依靠你的应用程序的用户来保护自己,这是比较明智的。

CSRF攻击假定一个用户已经登录到一个网络应用程序。他们被攻击者欺骗,打开一个包含脚本的页面,这些脚本在用户正在使用的一个应用程序中执行操作。

由于用户已经登录(正如我们从一开始就假定的那样),伪造代码现在可以冒充用户并代表他们进行操作。

我们如何保护我们的用户免受这种情况的影响?CSRF保护想要确保的是,只有Web应用程序的前端可以执行相应操作(按照惯例,除了GET、HEAD、TRACE或OPTIONS以外的HTTP方法)。

我们怎样才能实现这一点呢?你可以肯定的是,在能够做任何可能改变数据的动作之前,用户必须使用HTTP GET发送请求,至少要看一次网页。当这种情况发生时,应用程序会生成一个唯一的令牌。应用程序现在只接受头中包含这个唯一值的相应操作(POST、PUT、DELETE等)的请求。应用程序认为,知道令牌的值就证明了是应用程序本身在进行相应请求,而不是其他系统。任何包含相应调用的页面,如POST、PUT、DELETE等,都应该通过响应收到CSRF令牌,并且页面在进行相应调用时必须使用该令牌。

CSRF保护的起点是过滤器链中一个名为CsrfFilter的过滤器。CsrfFilter拦截请求并允许所有使用这些HTTP方法的请求。GET, HEAD, TRACE, 和 OPTIONS。对于所有其他请求,过滤器期望收到一个包含令牌的头。如果这个标头不存在或者包含一个不正确的标头值,应用程序会拒绝该请求,并将响应状态设置为HTTP 403 Forbidden。

这个令牌是什么,它来自哪里?这些令牌只不过是字符串值。当你使用GET、HEAD、TRACE或OPTIONS以外的任何方法时,你必须在请求的头中添加令牌。如果你不添加包含令牌的头,应用程序就不接受该请求,如图10.2所示。

image-20230127151827291

图10.2 要进行POST请求,客户端需要添加一个包含CSRF令牌的头。当页面被加载时(通过GET请求),应用程序会生成一个CSRF令牌,该令牌被添加到所有可以从加载的页面发出的请求中。这样一来,只有被加载的页面可以发出可变的请求。

CsrfFilter(图10.3)使用一个名为CsrfTokenRepository的组件来管理生成新令牌的CSRF令牌值,存储令牌,并最终使这些令牌失效。默认情况下,CsrfTokenRepository在HTTP会话上存储令牌,并将令牌生成为随机的通用唯一标识符(UUID)。在大多数情况下,这就足够了,但正如你将在第10.1.3节所了解的,如果默认的CsrfTokenRepository不适用于你需要实现的要求,你可以使用自己的实现。

image-20230127152324640

图10.3 CsrfFilter是过滤器链中的一个过滤器。它接收请求并最终将其转发给链中的下一个过滤器。为了管理CSRF令牌,CsrfFilter使用一个CsrfTokenRepository。

在本节中,我用大量的文字和数字解释了CSRF保护在Spring Security中是如何工作的。但我也想通过一个小的代码例子来加强你的理解。让我们创建一个暴露了两个api的应用程序。我们可以用HTTP GET调用其中一个,用HTTP POST调用另一个。正如你现在所知道的,如果不关闭CSRF保护,你就不能直接用POST调用端点。

正如你在这个例子中学到的,CsrfFilter将生成的CSRF令牌添加到HTTP请求中名为_csrf的属性中(图10.4)。如果我们知道这一点,我们就知道在CsrfFilter之后,我们可以找到这个属性并从中获取令牌的值。对于这个案例,我们选择在CsrfFilter之后添加一个自定义过滤器,正如你在第9章中学到的那样。你使用这个自定义过滤器在应用程序的控制台中打印CSRF令牌,当我们使用HTTP GET调用端点时,应用程序会生成CSRF令牌。然后我们可以从控制台复制令牌的值,用它来进行HTTP POST的调用。在下面的列表中,你可以找到控制器类的定义和我们用于测试的两个api。

代码清单10.1 有两个端点的控制器类

package com.hashnode.proj0001firstspringsecurity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Guowei Chi
 * @date 2023/1/19
 * @description:
 **/
@RestController
public class HelloController {
    
    
    @GetMapping("/hello")
    public String getHello() {
    
    
        return "Get Hello!";
    }

    @PostMapping("/hello")
    public String postHello() {
    
    
        return "Post Hello!";
    }
}

代码清单10.2定义了我们用来在控制台中打印CSRF令牌值的自定义过滤器。我把这个自定义过滤器命名为CsrfTokenLogger。当被调用时,该过滤器从_csrf请求属性中获取CSRF标记的值,并将其打印在控制台中。请求属性的名称,_csrf,是CsrfFilter将生成的CSRF令牌的值设置为CsrfToken类的一个实例的地方。这个CsrfToken实例包含CSRF令牌的字符串值。你可以通过调用getToken()方法来获得它。

清单10.2 自定义过滤器类的定义

package com.hashnode.proj0001firstspringsecurity.order;

import org.springframework.security.web.csrf.CsrfToken;

import javax.servlet.*;
import java.io.IOException;
import java.util.logging.Logger;

public class CsrfTokenLogger implements Filter {
    
    
    private Logger logger =
            Logger.getLogger(CsrfTokenLogger.class.getName());

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        //从_csrf请求属性中获取令牌的值,并将其打印在控制台。
        Object o = servletRequest.getAttribute("_csrf");
        CsrfToken token = (CsrfToken) o;
        logger.info("CSRF token " + token.getToken());
        filterChain.doFilter(servletRequest, servletResponse);
    }

}

在配置类中,我们添加了自定义过滤器。下一个代码清单展示了configuration类。请注意,我没有在清单中禁用CSRF保护。

代码清单10.3 在配置类中添加自定义过滤器

package com.hashnode.proj0001firstspringsecurity.config;

import com.hashnode.proj0001firstspringsecurity.order.CsrfTokenLogger;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CsrfFilter;

/**
 * @author Guowei Chi
 * @date 2023/1/19
 * @description:
 **/
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(HttpSecurity http)
            throws Exception {
    
    
        http.addFilterAfter(
                new CsrfTokenLogger(), CsrfFilter.class)
                .authorizeRequests()
                .anyRequest().permitAll();
    }
}

我们现在可以测试api了。由于CsrfTokenRepository接口的默认实现使用HTTP会话在服务器端存储令牌值,我们还需要记住会话ID。出于这个原因,我在调用中加入了-v标志,这样我就可以从响应中看到更多的细节,包括会话ID。

测试结果如下:

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.83.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=96636FF5093C03E83035C5748DB07E1B; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 10
< Date: Fri, 27 Jan 2023 08:05:56 GMT
<
Get Hello!* Connection #0 to host localhost left intact


# 在应用程序控制台的请求之后,你可以找到一个包含CSRF令牌的日志行。
2023-01-27 16:05:56.919  INFO 12316 --- [nio-8080-exec-1] c.h.p.order.CsrfTokenLogger              : CSRF token 841df929-0f27-4775-a60d-329f1049bfb6




如果你使用HTTP POST方法调用端点,而不提供CSRF令牌,响应状态是403 Forbidden,正如这个命令行所显示的。

curl -XPOST http://localhost:8080/hello

{
    
    
"status":403,
"error":"Forbidden",
"message":"Forbidden",
"path":"/hello"
}

但如果你为CSRF令牌提供了正确的值,调用就会成功。你还需要指定会话ID(JSESSIONID),因为CsrfTokenRepository的默认实现将CSRF令牌的值存储在会话上。

curl -X POST http://localhost:8080/hello -H 'Cookie: JSESSIONID=96636FF5093C03E83035C5748DB07E1B' -H 'X-CSRF-TOKEN: 841df929-0f27-4775-a60d-329f1049bfb6'

10.2 使用跨域共享 (CORS)

在本节中,我们将讨论跨源资源共享(CORS)以及如何用Spring Security应用它。首先,什么是CORS,为什么要关心?CORS的必要性来自于网络应用。默认情况下,浏览器不允许为网站加载的域以外的任何域发出请求。例如,如果你从example.com访问网站,浏览器不会让网站向api.example.com发出请求。图10.12显示了这个概念。

image-20230128093210351

图10.12 跨源资源共享(CORS)。当从example.com访问时,该网站不能向api.example.com发出请求,因为它们将是跨域请求。

我们可以简单地说,浏览器使用 CORS 机制来放松这种严格的政策,并允许在某些条件下在不同的源头之间发出请求。你需要知道这一点,因为你很可能要把它应用到你的应用程序中,尤其是在今天,前端和后端是独立的应用程序。常见的情况是,一个前端应用是使用Angular、ReactJS或Vue这样的框架开发的,并托管在example.com这样的域名上,但它调用托管在另一个域名,如api.example.com。在这一节中,我们开发了一些例子,你可以从中学习如何为你的网络应用应用CORS策略。我们还描述了一些你需要知道的细节,这样你就可以避免在你的应用程序中留下安全漏洞。

10.2.1 CORS是如何工作的?

在这一节中,我们讨论 CORS 如何应用于网络应用。例如,如果你是 example.com 的所有者,由于某种原因,example.org 的开发者决定从他们的网站调用你的 REST 接口,他们将不能这样做。例如,如果一个域名使用iframe加载你的应用程序,也会发生同样的情况(见图10.13)。

image-20230128093730490

图10.13 即使应用程序发出了请求,浏览器也不会接受响应。

iframe是一个HTML元素,用来把一个网页产生的内容嵌入到另一个网页中(例如,把example.org的content整合到example.com的网页中)。

任何应用程序在两个不同领域之间进行调用的情况都是被禁止的。但是,当然,你可以找到你需要进行这种调用的情况。在这些情况下,CORS允许你指定你的应用程序允许从哪个域发出请求,以及哪些细节可以被共享。CORS机制是基于HTTP头信息工作的(图10.14)。其中最重要的是

  • Access-Control-Allow-Origin-指定可以访问的域上资源的源。
  • Access-Control-Allow-Methods-让我们在想允许访问不同的域,但只允许访问特定的HTTP方法的情况下进行约束。例如,如果你要让example.com调用某个端点,但只用HTTP GET,你就可以使用这个方法。
  • Access-Control-Allow-Headers-对你在特定请求中可以使用的header添加限制。

image-20230128095149817

图10.14 启用跨源请求。example.org服务端添加了Access-Control- Allow-Origin头,以指定浏览器应该接受响应的请求的来源。如果调用的域名在来源中被列举出来,那么浏览器就会接受该响应。

在Spring Security中,默认情况下,这些头信息都不会被添加到响应中。所以让我们从头开始:如果你没有在你的应用程序中配置CORS,当你进行跨源调用时会发生什么。当应用程序发出请求时,它希望响应有一个Access-Control-Allow-Origin头,包含服务器接受的起源。如果这一点没有发生,就像默认的Spring Security行为那样,浏览器就不会接受响应。让我们用一个小的Web应用来证明这一点。

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

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

我们定义了一个控制器类,它有一个主页面的动作和一个REST api。 因为该类是一个普通的Spring MVC @Controller类,我们还必须在端点上明确添加@ResponseBody注释。下面的列表定义了这个controller。

此外,我们需要定义配置类,在这个配置类中,我们禁用CSRF保护,使这个例子更简单,让你只关注CORS机制。 另外,我们允许对所有的端点进行未经认证的访问。下一个代码清单定义了这个配置类。

代码清单10.20 配置类的定义

package com.hashnode.proj0001firstspringsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author Guowei Chi
 * @date 2023/1/19
 * @description:
 **/
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.csrf().disable();
        http.authorizeRequests()
                .anyRequest().permitAll();
    }
}

当然,我们也需要在项目的resources/templates文件夹中定义main.html文件。main.html文件包含调用/test api的JavaScript代码。为了模拟跨源调用,我们可以在浏览器中使用域名localhost访问该页面。从JavaScript代码中,我们使用IP地址127.0.0.1进行调用。即使localhost和127.0.0.1指的是同一个主机,浏览器也会把它们看作是不同的字符串,认为它们是不同的域。下一个代码清单定义了main.html页面。

代码清单10.21 main.html页面

<!DOCTYPE HTML>
<html lang="en">
<head>
    <script>
        const http = new XMLHttpRequest();
        const url='http://127.0.0.1:8080/test';
        http.open("POST", url);
        http.send();
        http.onreadystatechange = (e) => {
      
      
            document
                .getElementById("output")
                .innerHTML = http.responseText;
        }
    </script>
</head>
<body>
<div id="output"></div>
</body>
</html>

启动应用程序,在浏览器中用localhost:8080打开页面,我们可以观察到页面没有显示任何东西。我们期望在页面上看到HELLO,因为这就是/test端点返回的内容。当我们检查浏览器的控制台时,我们看到的是一个由JavaScript调用打印的错误。这个错误看起来像这样。

image-20230128101940276

错误信息告诉我们,响应没有被接受,因为Access- Control-Allow-Origin HTTP头不存在。这种行为的发生是因为我们没有在Spring Boot应用中配置任何有关CORS的东西,而且默认情况下,它不会设置任何与CORS有关的header。因此,浏览器不显示响应的行为是正确的。然而,我想让你注意到,在应用程序的控制台中,日志证明了该方法被调用。下一个代码片断显示了你在应用程序控制台中发现的情况。

2023-01-28 10:19:17.905 INFO 18004 — [nio-8080-exec-1] c.h.p.controller.MainController : Test method called

这方面很重要!我遇到很多开发者,他们把 CORS 理解为类似于授权或 CSRF 保护的限制。CORS不是一种限制,而是帮助放松跨域调用的刚性约束。而且,即使应用了限制,在某些情况下,api也可以被调用。这种行为并不总是发生。 有时,浏览器首先使用HTTP OPTIONS方法进行调用,以测试该请求是否应该被允许。我们称这种测试请求为预检请求。如果预检请求失败,浏览器就不会尝试原始请求。

预检请求以及是否进行预检的决定是浏览器的责任。你不需要实现这个逻辑。但理解它是很重要的,所以即使你没有为特定的域指定任何CORS策略,你也不会惊讶地看到对后端的跨源调用。当你有一个用Angular或ReactJS等框架开发的客户端应用时,也可能发生这种情况。图10.15展示了这种请求流。

image-20230128102636664

图10.15 对于简单的请求,浏览器直接向服务器发送原始请求。如果服务器不允许该api,浏览器就会拒绝响应。在某些情况下,浏览器会发送一个预检请求来测试服务器是否接受原点。如果预检请求成功,浏览器就会发送原始请求。

在我们的例子中,浏览器发出请求,但如果响应中没有指定来源,我们就不接受响应,如图 10.9 和 10.10 所示。CORS机制归根结底与浏览器有关,而不是保证api安全的方法。它唯一能保证的是,只有你允许的原点域能从浏览器的特定页面发出请求。

10.2.2 使用 @CrossOrigin 注解来应用 CORS 策略。

在这一节中,我们讨论如何使用 @CrossOrigin 注解来配置 CORS 以允许来自不同领域的请求。你可以把 @CrossOrigin 注解直接放在定义api的方法上面,并使用允许的origin和方法对其进行配置。正如你在本节中所学习的,使用 @CrossOrigin 注解的好处是,它使得为每个api配置 CORS 非常容易。

我们使用在第10.2.1节中创建的应用程序来演示@Cross-Origin如何工作。为了使跨源调用在应用程序中发挥作用,你唯一需要做的就是在控制器类的test()方法上添加@CrossOrigin注解。下面的列表显示了如何使用注解来使localhost成为允许的原点。

代码清单10.22 使 localhost 成为允许的origin

//定义了一个api,我们从不同的源头调用,以证明CORS是如何工作的。
@PostMapping("/test")
@ResponseBody
@CrossOrigin("http://localhost:8080")
public String test() {
    
    
    logger.info("Test method called");
    return "HELLO";
}

你可以重新运行并测试该应用程序。现在应该在页面上显示由/test api返回的字符串:HELLO。

@CrossOrigin的值参数接收一个数组,让你定义多个起源;例如,@CrossOrigin({“example.com”, “example.org”}) 。你还可以使用注解的allowedHeaders属性和methods属性来设置允许的header和方法。对于origin和header信息,你可以使用星号(*)来代表所有header信息或所有origin。但是我建议你谨慎使用这种方法。最好的办法是过滤你想允许的起源和头文件,而绝不允许任何域实现访问你的应用程序资源的代码。

如果允许所有的来源,你就会把应用程序暴露在跨网站脚本(XSS)请求之下,这最终会导致DDoS攻击,正如我们在第一章所讨论的那样。我个人甚至在测试环境中也避免允许所有来源。

使用@CrossOrigin来直接指定api访问的规则,其优点是可以创造良好的规则透明度。缺点是,它可能会变得冗长,迫使你重复大量的代码。它还带来了这样的风险:开发者可能会忘记为新实现的端点添加注释。在第 10.2.3 节,我们讨论了在配置类中集中应用 CORS 配置。

10.2.3 使用CorsConfigurer应用CORS

尽管使用 @CrossOrigin 注解很简单,正如你在第 10.2.2 节中所学到的,你可能发现在很多情况下,在一个地方定义 CORS 配置会更舒服。在这一节中,我们改变了在第 10.2.1 和 10.2.2 节中的例子,在配置类中使用一个 Customizer 来应用 CORS 配置。在下一个代码清单中,你可以找到我们需要在配置类中做出的改变,以定义我们想要允许的源。

package com.hashnode.proj0001firstspringsecurity.config;

import com.google.common.collect.Lists;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;


/**
 * @author Guowei Chi
 * @date 2023/1/19
 * @description:
 **/
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.cors(c -> {
    
    
            CorsConfigurationSource source = request -> {
    
    
                CorsConfiguration config = new CorsConfiguration();
                config.setAllowedOrigins(
                        Lists.newArrayList("example.com", "example.org"));
                config.setAllowedMethods(
                        Lists.newArrayList("GET", "POST", "PUT", "DELETE"));
                return config;
            };
            c.configurationSource(source);
        });
        http.csrf().disable();
        http.authorizeRequests()
                .anyRequest().permitAll();
    }
}

我们从HttpSecurity对象中调用的cors()方法接收一个Customizer对象作为参数。对于这个对象,我们设置个CorsConfigurationSource,它为一个HTTP请求返回CorsConfiguration。 CorsConfiguration是一个说明允许的起源、方法和头文件的对象。如果你使用这种方法,你必须至少指定哪些是来源和方法。如果你只指定origin,你的应用程序将不允许请求。这种行为的发生是因为CorsConfiguration对象默认没有定义任何方法。

在这个例子中,为了使解释简单明了,我把CorsConfigurationSource的实现直接作为configure()方法中的一个lambda表达式。我强烈建议在你的应用程序中把这段代码分离到不同的类中。在现实世界的应用中,你可能会有更长的代码,所以如果不被配置类分开,就会变得难以阅读。

总结

  • 跨站请求伪造(CSRF)是一种攻击类型,用户被欺骗访问一个包含伪造脚本的页面。这个脚本可以冒充登录到一个应用程序的用户,并代表他们执行操作。
  • CSRF在Spring Security中是默认启用的。
  • 在Spring Security架构中,CSRF保护逻辑的入口是一个HTTP过滤器。
  • 跨域共享(CORS)是指在一个特定域上托管的网络应用程序试图访问另一个域的内容的情况。默认情况下,浏览器不允许这种情况发生。CORS配置使你能够允许在浏览器中运行的网络应用程序中从不同的域调用你的部分资源。
  • 你可以使用@CrossOrigin注释为一个端点配置CORS,也可以使用HttpSecurity对象的cors()方法在配置类中集中配置。

猜你喜欢

转载自blog.csdn.net/Learning_xzj/article/details/128776344