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

本文将详细介绍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开发中如何使用JVisualVM进行性能分析

    完整攻略如下: 1. 什么是JVisualVM JVisualVM是Java SE平台的一个工具,用于监视本地或远程的Java程序运行状态。使用JVisualVM可以实时监控Java程序的内存、CPU、线程等实时状态,同时支持通过插件扩展功能。 2. 如何使用JVisualVM进行性能分析 2.1. 下载并安装JVisualVM JVisualVM是Java…

    Java 2023年5月26日
    00
  • Eclipse中使用Maven创建Java Web工程的实现方式

    创建Java Web工程有多种方式,其中一种常用的方式就是使用Maven构建工具。下面我将详细讲解如何在Eclipse中使用Maven创建Java Web工程的实现方式,包括如下几个步骤: 安装Maven插件 创建Maven Web项目 添加Web和Servlet依赖 编写Servlet程序 配置并运行Tomcat服务器 下面逐一进行详细讲解: 1. 安装M…

    Java 2023年5月20日
    00
  • 详解Spring Data JPA使用@Query注解(Using @Query)

    当我们使用Spring Data JPA进行数据库操作时,我们可以使用@Query注解来定制自己的SQL语句。本文将详细讲解@Query注解的使用方法。 1. @Query注解概述 @Query注解可以被用来定义查询语言或者存储过程语言,以代替基于方法名的查询语句。通过使用@Query,可以使用JPQL或者本地SQL来执行查询。该注解用于在JPA Repos…

    Java 2023年5月20日
    00
  • Java字节码插装的作用是什么?

    Java字节码插装是指在程序运行期间通过修改Java程序的字节码来达到修改程序行为和进行调试的目的。常见的字节码插装技术有Java Agent和AspectJ。 Java字节码插装的作用主要分为以下两个方面: 类加载时期修改类的字节码,在程序运行时对其进行增强 在程序运行时,通过对方法的字节码进行修改,实现将自己的代码嵌入到目标方法的中间或结尾位置 常见的应…

    Java 2023年5月11日
    00
  • java object 之clone方法全面解析

    Java对象之clone方法全面解析 简介 在Java中,如果使用赋值号将一个对象赋值给另外一个对象,那么这两个对象会共用同一份数据。而通过clone()方法可以创建一个新的对象,并复制原始对象数据到新对象中。 在本篇文章中,我们将全面解析clone()方法,介绍如何使用clone()方法拷贝一个Java对象。 clone() 方法说明 clone()方法是…

    Java 2023年5月26日
    00
  • Java Spring框架创建项目与Bean的存储与读取详解

    Java Spring 框架是目前应用非常广泛的一种开发框架,它提供了很多便捷的功能和技术来协助我们进行项目开发。Spring 框架的核心部分就是它的 IoC(控制反转) 容器,它是 Spring 框架的一个轻量级容器,用于管理应用程序中所依赖的各种对象。在本文中,我们将介绍如何使用 Spring 来创建项目,并详解如何使用 Spring 的 IoC 容器来…

    Java 2023年5月19日
    00
  • springboot 基于Tomcat容器的自启动流程分析

    Spring Boot 基于 Tomcat 容器的自启动流程分析 1. 概述 在 Spring Boot 应用程序中,Tomcat 是一个常用的内嵌式 Web 服务器,它可以很方便地帮助我们创建和启动 Web 应用程序。在本文中,我们将深入探究 Spring Boot 基于 Tomcat 容器的自启动流程。 2. Tomcat 自启动流程 在 Spring …

    Java 2023年6月15日
    00
  • 利用json2POJO with Lombok 插件自动生成java类的操作

    利用json2POJO with Lombok插件自动生成Java类是一个方便快捷的方式,特别是在进行大量API接口开发的时候。下面是使用该插件的完整攻略。 1. 下载插件 首先,需要在Intellij IDEA中安装json2POJO with Lombok插件。可以通过Intellij IDEA的插件市场来搜索和安装该插件。 2. 生成Java类 在In…

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