Spring Boot 3 集成 Spring Security(1)认证

文章目录

    • [1. 简介与概念](#1. 简介与概念)
    • [2. 基础配置](#2. 基础配置)
      • [2.1. 添加依赖](#2.1. 添加依赖)
      • [2.1 基本认证与授权配置](#2.1 基本认证与授权配置)
    • [3. 密码加密](#3. 密码加密)
      • [3.1. 如何加密用户密码](#3.1. 如何加密用户密码)
      • [3.2. 自定义密码加密器](#3.2. 自定义密码加密器)
    • [4. 表单登录与自定义登录页面](#4. 表单登录与自定义登录页面)
      • [4.1. 自定义表单登录配置](#4.1. 自定义表单登录配置)
      • [4.2. 定义控制器](#4.2. 定义控制器)
      • [4.3. 自定义登录界面](#4.3. 自定义登录界面)
      • [4.4. 测试登录](#4.4. 测试登录)
      • [4.5. 注销](#4.5. 注销)

Spring Security 是一个强大、灵活的安全框架,广泛用于保护 Java 应用程序。随着 Spring Boot 3 和 Java 17 的引入,Spring Security 继续增强其功能,为开发者提供了更简化的配置和现代化的安全实践。

本文将详细介绍如何在 Spring Boot 3 中集成 Spring Security,涵盖基本认证、密码加密等核心功能。

1. 简介与概念

Spring Security的重要核心功能功能是"认证 "和"授权 ",即**用户认证(Authentication)用户授权(Authorization)**两部分:

(1)用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求提供用户名和密码,系统通过校验用户名和密码来完成认证过程。

(2)用户授权:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

Spring Security的特点

  • 和Spring无缝整合
  • 全面的权限控制
  • 专门为Web开发而设计
  • 重量级

Spring Security实现权限

要对Web资源进行保护,最好的办法莫过于Filter

要对方法调用进行保护,最好的方法莫过于AOP

Spring Security进行认证和鉴权的时候就是利用一系列的Filter进行拦截的。

如图所示,一个请求要想访问到API就会从左到右经过蓝线框里的过滤器,其中绿色部分是负责认证的过滤器,蓝色部分就是负责异常处理,橙色部分则是负责授权。经过一系列拦截最终访问到我们的API。

  • FilterSecurityInterceptor:是一个方法级的过滤器,基本位于过滤链的最底部。
  • ExceptionTranslationFilter:是一个异常过滤器,用来处理在认证授权过程中抛出的异常。
  • UsernamePasswordAuthenticationFilter :对/login的POST请求做拦截,校验表单中用户名、密码。

这里我们只需要重点关注两个过滤器即可:UsernamePasswordAuthenticationFilter负责登陆认证,FilterSecurityInterceptor负责权限授权。

用户认证流程

自定义组件

根据认证流程,我们需要自定义以下组件:

  • UserDetails
  • loadUserByUsername
  • passwordEncoder

1、登陆Filter,判断用户名和密码是否正确,生成token

2、认证解析token组件,判断请求头是否有token,如果有认证完成

3、在配置类配置相关认证类


2. 基础配置

Spring Boot 3 使用自动配置来简化 Spring Security 的集成。但在许多实际场景中,我们需要自定义安全配置,下面介绍基本的 Spring Security 配置步骤。

2.1. 添加依赖

首先,在 pom.xml 中添加 Spring Security 依赖:

xml 复制代码
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

接着,在 application.yml 中配置安全设置。

2.1 基本认证与授权配置

首先,我们通过创建 SecurityConfig 类来自定义 Spring Security 的配置。该配置类通常需要实现 SecurityFilterChain,Spring Security 通过过滤器链的方式来处理 HTTP 请求。过滤器链由一系列的过滤器 (Filter) 组成,这些过滤器按照配置的顺序依次处理请求。每个过滤器完成特定的安全检查或操作(如身份验证、授权、会话管理等),然后将请求传递给下一个过滤器。

示例代码

java 复制代码
package cn.harry.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @author harry
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        // 公开访问
                        .requestMatchers("/").permitAll()
                        // 其他接口需认证
                        .anyRequest().authenticated()
                )
                // 使用默认的登录页面
                .formLogin(Customizer.withDefaults())
                // 使用 HTTP Basic 认证
                .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        // 创建用户
        UserDetails user = User.builder()
                .username("admin")
                .password(passwordEncoder.encode("123456"))
                .roles("ROOT")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用 BCrypt 进行密码加密
        return new BCryptPasswordEncoder();
    }
}

解释

  1. SecurityFilterChain 定义了所有 HTTP 请求的安全策略。在这里,/ 路径对所有人公开,而其他路径需要用户身份认证。

  2. UserDetailsService 提供了用户的详细信息,包括用户名、密码及角色。在这个例子中,我们创建了一个用户名为 "admin" 的用户,密码为 "123456"(经过加密处理),并分配了 "ROOT" 角色。

    如果不配置,系统则会在控制台中输出名为 user 的用户对应的密码:Using generated security password: 147f9f11-e6c2-4cc6-8253-10b3f28bb5ff

  3. PasswordEncoder 通过 BCryptPasswordEncoder 实现密码加密,以确保用户密码存储时是安全的。

  4. @EnableWebSecurity注解启动 Spring Security 的自动配置,使得应用能够自动集成 Spring Security 提供的安全功能。

3. 密码加密

Spring Security 强烈建议使用加密算法对密码进行加密,防止敏感信息泄露。在 Spring Boot 3 中,BCryptPasswordEncoder 是一种常用的加密方式。它基于 bcrypt 算法,提供了足够的强度和安全性。

3.1. 如何加密用户密码

  • UserDetailsService 中,我们通过 passwordEncoder.encode("password") 对用户密码进行加密。
  • 在身份验证时,Spring Security 会自动使用同样的加密算法进行密码比对。

3.2. 自定义密码加密器

如果需要自定义密码加密算法,可以实现 PasswordEncoder 接口。以下是自定义加密器的简单示例:

typescript 复制代码
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(10);  // 设置加密强度
}

在这个示例中,我们为 BCryptPasswordEncoder 提供了加密强度参数,值越大,安全性越高,但加密速度会相对减慢。BCryptPasswordEncoder 的默认实现使用 BCryptPasswordEncoder 的 Javadoc 中提到的强度10。

pgsql 复制代码
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
System.out.println(encoder.matches("password", result));

或者使用 SpringBoot CLI 对密码进行加密:

crystal 复制代码
$ spring encodepassword password

4. 表单登录与自定义登录页面

除了 Basic 认证,Spring Security 还支持表单登录。通过 formLogin() 方法,可以启用表单认证,也提供自定义的登录页面。

stylus 复制代码
http
        .formLogin(Customizer.withDefaults() // 使用默认登录

4.1. 自定义表单登录配置

less 复制代码
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
  			.requestMatchers("/").permitAll()  // 公开访问
            .anyRequest().authenticated()
        )
        .formLogin(form -> form
            .loginPage("/mylogin")  // 自定义登录页面
            .loginProcessingUrl("/login")
            .permitAll()  // 登录页面无需认证
        )
        .logout(logout -> logout.permitAll());  // 允许注销
    return http.build();
}

在这里,我们自定义了登录页面 /login,并允许用户访问登录功能而无需认证,提供接口 /index 返回 index.html 页面:

typescript 复制代码
    @GetMapping("/mylogin")
    public String login() {
        // 返回名为 "login" 的模板或 HTML 页面
        return "mylogin";
    }

4.2. 定义控制器

java 复制代码
package cn.harry.demo.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author harry
 */
@Slf4j
@Controller
public class LoginController {

    @GetMapping("/")
    public String home() {
        log.info("index");
        // 返回名为 "index" 的模板或 HTML 页面
        return "index";
    }

    @GetMapping("/mylogin")
    public String login() {
        // 返回名为 "login" 的模板或 HTML 页面
        return "mylogin";
    }


    @GetMapping("/main")
    public String main() {
        // 返回名为 "main" 的模板或 HTML 页面
        return "main";
    }

}

4.3. 自定义登录界面

提供一个简单的首页页面(index.html)、受限资源(main.html)和登录页(mylogin.html),这里使用了 thymeleaf 模板:

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>自定义登录页面</title>
</head>
<body>
<h1>login</h1>
<form method="post" th:method="post" th:action="@{/login}">
    <label>
        <input type="text" name="username" placeholder="username" />
    </label>
    <br>
    <label>
        <input type="password" name="password" placeholder="password" />
    </label>
    <br>
    <input type="submit" value="login">
</form>
</body>
</html>

4.4. 测试登录

进入首页,此时不需要登录!

点击 访问受限资源 /main 要求认证,进入我们自定义的登录页。

输入设置的用户名和密码之后,认证通过,进入 /main 页面:

4.5. 注销

有登录,同样也提供了登出,默认情况下,Spring Security 会建立一个 /logout 端点,所以不需要额外的代码。当你包含 spring-boot-starter-security 依赖或使用 @EnableWebSecurity 注解时,Spring Security 将添加其注销支持,并默认响应 GET /logoutPOST /logout

注意,Spring Security 默认开启了防 CSRF 攻击,注销时需要提供 csrf_token,直接调用 GET /logout 会提示 404,只能能通过 POST 请求,

html 复制代码
<form th:action="@{/logout}" method="post">
    <button type="submit">Logout</button>
</form>

如果强制要使用 GET 请求,则需要修改成以下配置形式:

java 复制代码
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(Customizer.withDefaults())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/").permitAll()  // 公开访问
                        .anyRequest().authenticated()  // 其他接口需认证
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .permitAll()
                )
                .logout((logout) -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                )
                .httpBasic(Customizer.withDefaults());  // 使用 HTTP Basic 认证
        return http.build();
    }

本文介绍了如何在Spring Boot 3应用中整合Spring Security,Security用法再之前的版本中有一些变化,请参考《Spring Boot 2 和 Spring Boot 3 中使用 Spring Security 的区别》

代码参考:Spring Boot 3 集成 Spring Security

相关推荐
用户908324602732 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840823 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解3 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解3 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记3 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者4 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840824 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解4 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者5 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺5 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端