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

相关推荐
m0_748255262 小时前
Redis四种模式在Spring Boot框架下的配置
spring boot·redis·bootstrap
m0_748248772 小时前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
荆州克莱3 小时前
Windows 安装 MySQL8(在已有MySQL 5.7 的情况下)
spring boot·spring·spring cloud·css3·技术
咕德猫宁丶3 小时前
Spring Boot + MinIO 实现分段、断点续传,让文件传输更高效
java·spring boot·后端·中间件
阿新-3 小时前
解决Spring boot集成quartz时service注入失败为null的问题
java·spring boot·后端
重整旗鼓~4 小时前
8.若依系统监控与定时任务
java·spring boot
梦想blog5 小时前
Spring Boot + Redisson 封装分布式锁
spring boot·分布式·后端·
ss2735 小时前
2025元旦源码免费送
spring boot·微信小程序·开源
武昌库里写JAVA5 小时前
mysql乱码、mysql数据中文问号
java·开发语言·spring boot·学习·课程设计
杨荧5 小时前
【开源免费】基于Vue和SpringBoot的网上商城系统(附论文)
前端·javascript·jvm·vue.js·spring boot·spring cloud·开源