SpringSecurity的防Csrf攻击实现代码解析

yizhihongxing

本文将详细介绍Spring Security中防范Csrf攻击的实现代码解析。

什么是Csrf攻击

Csrf全称为Cross-site request forgery,即跨站请求伪造。它利用用户在已经登录的网站中的权限来进行恶意攻击,而用户却毫不知情。攻击者可以通过各种方式获取并篡改用户的Cookie,再利用这些Cookie发起跨站请求伪造攻击,使得受害者被误导到执行某些意料之外的操作。

SpringSecurity中的Csrf攻击防御

SpringSecurity中提供了防范Csrf攻击的实现,包括Token的生成和验证。当用户访问页面时,服务器会将一个固定的Token放在该页面的表单中或者请求头中,在后续的请求中,服务器会校验这个Token是否匹配,如果不匹配,则拒绝该请求。这样就防止了Csrf攻击。

SpringSecurity中Csrf攻击防御的实现

1.启用Csrf攻击防御

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  ...
  @Override protected void configure(HttpSecurity http) throws Exception {
    ...
    http.csrf().csrfTokenRepository(csrfTokenRepository())
    .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
  }
  ...
}

在Spring Security中启用Csrf攻击的防御,需要在configure方法中配置http.csrf()。需要注意的是,csrf()方法返回的是一个CsrfConfigurer对象。通过其csrfTokenRepository方法可以设置此对象用于持久化CsrfToken的CsrfTokenRepository,而通过csrfHeaderFilter方法可以指定当Csrf攻击发生时,Spring Security应该使用哪个Filter对它进行处理。

2.CsrfToken的生成和存储

public class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {
  ...
  public CsrfToken generateToken(HttpServletRequest request) {
    String token = UUID.randomUUID().toString();
    return new DefaultCsrfToken(this.headerName, this.parameterName, token);
  }
  ...
}

Spring Security提供了默认的CsrfTokenRepository——HttpSessionCsrfTokenRepository。在此对象中,generateToken方法会生成一个固定的CsrfToken,并通过DefaultCsrfToken类进行封装,然后将封装后的CsrfToken存储在HttpSession中。

3.前端获取CsrfToken

var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");

$(document).ajaxSend(function(e, xhr, options) {
  xhr.setRequestHeader(header, token);
});

为了获取服务器生成的CsrfToken,需要在前端页面中引入一个标签,标签的name属性为_csrf,在SpringSecurity中,会将CsrfToken存储到这个name属性对应的content中。

4.后端校验CsrfToken

public class CsrfFilter extends OncePerRequestFilter {
  ...
  protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    ...
    CsrfToken csrfToken = this.csrfTokenRepository.loadToken(request);
    ...
    if (csrfToken == null) {
      if ("GET".equals(request.getMethod())) {
        ...
      } else {
        ...
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Missing CSRF token");
        return;
      }
    }
    ...
  }
  ...
}

在处理请求的Controller中,Spring Security会自动调用CsrfFilter过滤器来校验该请求中的CsrfToken与之前存储在HttpSession中的CsrfToken是否一致。如果不一致,则会给用户返回一个403的Not Accessible错误。

示例

示例1:前后端分离的Csrf防御

假设前后端分离的SpringBoot项目结构如下:

my-spring-boot
├── src
|   ├── main
|   |   ├── java
|   |   |   ├── com.example
|   |   |   |   ├── web
|   |   |   |   |   ├── ApiController.java
|   |   |   |   ├── MySpringBootApplication.java
|   |   ├── resources
|   |   |   ├── static
|   |   |   |   ├── csrf-example.html

对于这个场景,需要在前端页面生成CsrfToken,然后在Ajax中发送请求时携带CsrfToken。具体代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="_csrf" th:content="${_csrf.token}" />
    <meta name="_csrf_header" th:content="${_csrf.headerName}" />
    <title>CSRF Example</title>
  </head>
  <body>
    <h1>Spring Boot CSRF Example</h1>

    <button id="ajax-get-btn">GET Request</button>
    <button id="ajax-post-btn">POST Request</button>

    <script src="/webjars/jquery/jquery.min.js"></script>
    <script>
      $(function () {
        $('#ajax-get-btn').click(function() {
          $.ajax({
            url: '/api/hello?name=GET',
            type: 'GET',
            success: function(data) {
              $('#data').show();
              $('#data').text(data);
            }
          });
        });

        $('#ajax-post-btn').click(function() {
          $.ajax({
            url: '/api/hello?name=POST',
            type: 'POST',
            data: {},
            success: function(data) {
              $('#data').show();
              $('#data').text(data);
            }
          });
        });
      });
    </script>
    <div id="data"></div>
  </body>
</html>

在ApiController中,需要通过@PreAuthorize注解将请求限制为USER角色,防止不必要的请求。代码如下:

@RestController
@RequestMapping("/api")
public class ApiController {
  @GetMapping("/hello")
  @PreAuthorize("hasRole('USER')")
  public String helloGet() {
    return "hello GET";
  }

  @PostMapping("/hello")
  @PreAuthorize("hasRole('USER')")
  public String helloPost() {
    return "hello POST";
  }
}

示例2:Thymeleaf与SpringSecurity的Csrf防御

假设当前项目使用了Thymeleaf模板引擎,结构如下:

my-spring-boot
├── src
|   ├── main
|   |   ├── java
|   |   |   ├── com.example
|   |   |   |   ├── MySpringBootApplication.java
|   |   ├── resources
|   |   |   ├── templates
|   |   |   |   ├── csrf-example.html

对于这个场景,可以使用input标签的hidden类型来生成CsrfToken。具体代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <meta name="_csrf" th:content="${_csrf.token}" />
    <meta name="_csrf_header" th:content="${_csrf.headerName}" />
    <title>CSRF Example</title>
  </head>
  <body>
    <h1>Spring Boot CSRF Example</h1>
    <form action="#" th:action="@{/logout}" method="post">
      <input type="submit" value="Logout" />
      <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
    </form>
  </body>
</html>

需要注意的是,在模板引擎中必须使用SpringSecurity提供的ThymeleafDialect,此Dialect会帮助我们自动生成hidden类型的input标签。代码如下:

@Configuration
public class ThymeleafConfig {
  @Bean
  public SpringSecurityDialect securityDialect() {
    return new SpringSecurityDialect();
  }
}

通过在Thymeleaf模板中调用巨量其提供的<sec:csrfInput />标签即可自动生成CsrfToken:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
  <head>
    <meta charset="UTF-8" />
    <title>CSRF Example</title>
  </head>
  <body>
    <h1>Spring Boot CSRF Example</h1>
    <form action="#" th:action="@{/logout}" method="post">
      <input type="submit" value="Logout" />
      <sec:csrfInput />
    </form>
  </body>
</html>

结论

通过上述对SpringSecurity中防止Csrf攻击的实现代码解析,我们了解到SpringSecurity具有非常优秀的Csrf防御能力,同时也描述了如何在前后端分离的和Thymeleaf模板引擎的场景下使用CsrfToken。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity的防Csrf攻击实现代码解析 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 详细总结Java创建文件夹的方法及优缺点

    详细总结Java创建文件夹的方法及优缺点 在Java中,创建文件夹是一个常见的操作,无论是在后端开发还是在桌面应用程序中都很常用。本文将详细总结Java创建文件夹的方法及优缺点,包括三种方法。 方法一:使用File类的mkdir() File类是Java中的一个常用文件操作类,其中的mkdir()方法可以用于创建一个新的文件夹。 File file = ne…

    Java 2023年5月20日
    00
  • Mybatis与Jpa的区别和性能对比总结

    Mybatis与JPA的区别 定义 MyBatis是一个开源的ORM框架,它支持定制化SQL、存储过程以及高级映射。同时提供了缓存机制,可以优化数据库访问性能。 而JPA(Java Persistence API)是一个规范,不是具体的实现。它基于ORM(Object-Relational Mapping,对象关系映射)思想,将数据库中的表映射成Java对象…

    Java 2023年5月20日
    00
  • Spring Boot中使用Spring-data-jpa实现数据库增删查改

    下面是关于“Spring Boot中使用Spring-data-jpa实现数据库增删查改”的完整攻略,包括以下内容: 前置条件 引入依赖 创建实体类 创建Repository接口 使用Repository接口实现数据库的增删查改 示例1:新增数据 示例2:查询数据 1. 前置条件 在使用Spring-data-jpa实现数据库操作之前,需要保证本地环境已经安…

    Java 2023年5月20日
    00
  • 什么是G1收集器?

    G1 (Garbage-First)收集器是一款面向服务器端的垃圾收集器,它是JDK 9之后默认的垃圾收集器。与CMS和Parallel Scavenge收集器相比,G1收集器具有更好的吞吐量和更短的暂停时间。接下来,我们将详细讲解G1收集器的使用攻略,包括以下内容: G1收集器的优势和适用场景 G1收集器的参数调优 G1收集器的使用示例 G1收集器的优势和…

    Java 2023年5月10日
    00
  • Java服务器主机信息监控工具类的示例代码

    下面是Java服务器主机信息监控工具类的示例代码的完整攻略: 1.需求分析 我们需要编写一款可以监控Java服务器主机信息的工具类。在使用这个工具类时,我们希望能够: 获取系统CPU、内存的使用情况; 获取系统硬盘的使用情况; 获取系统网络带宽的使用情况。 2.技术选型 我们可以选择使用Java中的一些相关API实现这个功能,如: Runtime和Manag…

    Java 2023年5月30日
    00
  • JAVA学习进阶篇之时间与日期相关类

    JAVA学习进阶篇之时间与日期相关类 在Java中,有许多时间与日期相关的类,如Date、Calendar、SimpleDateFormat等,这些类能够方便地进行时间和日期的转换和操作。本篇文章将介绍Java中的时间与日期相关类的使用方法及其常用操作。 1. Date 类 Date 类是一个包含日期和时间的对象,在Java中非常基础和常用,可以用于表示当前…

    Java 2023年5月20日
    00
  • java二分查找插入法

    当需要在已排序数组中查找元素时,可以使用二分查找算法。如果需要向已排序数组中插入元素,可以使用二分查找插入法。 二分查找插入法的主要思路是通过二分查找找到需要插入的元素在数组中的位置,然后将该元素插入到该位置中。以下是具体的步骤: 首先,定义需要查询的元素 target 和已排序的数组 nums,同时记录数组的左右端点 left 和 right。 计算需要查…

    Java 2023年5月19日
    00
  • Jaspersoft Studio添加mysql数据库配置步骤

    下面我来详细讲解“Jaspersoft Studio添加mysql数据库配置步骤”的完整攻略,过程中我将会包含两条示例说明。 1. 下载MySQL JDBC驱动程序 Jaspersoft Studio需要通过JDBC连接到MySQL数据库,因此需要下载MySQL JDBC驱动程序。在MySQL官网下载页面(https://dev.mysql.com/down…

    Java 2023年6月16日
    00
合作推广
合作推广
分享本页
返回顶部