【Java进阶】Spring Security详解

🍂 枫言枫语 :我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。

"予一人以深耕,观万木之成枫。"

在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。
简介: Spring Security 是基于过滤器链的成熟安全框架,提供认证、鉴权及防御 CSRF 等攻击的核心功能,支持多种认证方式与灵活的权限控制模型。

|----------------|--------------------|-----------------|------------------|----------------------|
| Spring Boot 版本 | Spring Security 版本 | Java 最小版本 | Jakarta EE 规范 | 状态/建议 |
| 4.0.x | 7.0.x | Java 17 (推荐 25) | Jakarta EE 11 | 最新代次,支持 Passkeys、OTT |
| 3.4 / 3.5 | 6.4 / 6.5 | Java 17 | Jakarta EE 10 | 主流稳定,目前企业新项目首选 |
| 3.0 - 3.3 | 6.0 - 6.3 | Java 17 | Jakarta EE 10 | 稳定版,彻底废弃了旧版继承配置 |
| 2.1 - 2.7 | 5.1 - 5.8 | Java 8 / 11 | Java EE 8 / J2EE | 维护/旧版,仍有大量老项目在使用 |
| 1.5.x | 4.2.x | Java 7 / 8 | J2EE | 已过期,极老项目可见 |

更详细的介绍https://blog.csdn.net/m0_58782205/article/details/156912779

Spring Security (在 Spring Boot 环境中通常被称为 Spring Boot Security)是 Spring 家族中专门负责安全的一个子项目。

简单来说,它是为基于 Spring 的应用程序提供 身份验证(Authentication)授权(Authorization) 以及 常见攻击防护 的行业标准框架。


一、核心功能

Spring Security 主要解决两个核心问题:

  • 认证(Authentication): 确认"你是谁"。例如:通过用户名/密码登录、手机验证码登录、OAuth2(微信、GitHub 登录)等。

  • 授权(Authorization): 确认"你能做什么"。例如:管理员可以删除用户,而普通用户只能查看自己的个人资料。

此外,它还提供了防范常见 Web 攻击的能力,如:

  • CSRF(跨站请求伪造)@

  • Session Fixation(会话固定攻击)@

  • XSS(跨站脚本攻击)@


二、核心工作原理

Spring Security 的底层完全基于 Servlet 过滤器( Filters 。当你发起一个请求时,它会经过一系列的过滤器链(Security Filter Chain),每个过滤器负责不同的安全逻辑。

关键组件:

  1. SecurityFilterChain:包含一组过滤器,决定请求是否需要经过认证,或者是否符合特定的访问权限。

  2. AuthenticationManager:认证管理器,负责协调不同的认证方式(如从数据库查用户信息,或从 LDAP 查)。

  3. UserDetailsService:核心接口,用于从数据库或其他来源加载用户的详细信息(用户名、密码、权限)。

  4. SecurityContextHolder:用于存储当前已认证用户的详细信息,你可以在代码的任何地方获取当前登录的用户。


三、为什么在 Spring Boot 中使用它?

在没有 Spring Boot 之前,配置 Spring Security 非常繁琐(需要大量的 XML 或 Java 配置)。

Spring Boot 通过 spring-boot-starter-security 极大地简化了这一过程:

  • 开箱即用:只要引入依赖,你的所有接口都会被默认保护起来,必须登录才能访问。

  • 自动配置 :它会自动配置一个默认的用户(用户名为 user,密码在控制台打印)。

  • 易于定制 :你可以通过继承 WebSecurityConfigurerAdapter(旧版)或声明 SecurityFilterChain Bean(新版)轻松修改安全策略。


1.认证 vs 授权:对比

|------|-----------------------|--------------------|
| 特性 | 身份验证 (Authentication) | 授权 (Authorization) |
| 定义 | 确认用户的身份 | 确认用户拥有的操作权限 |
| 常见方式 | 账号密码、JWT、OAuth2、双重认证 | 基于角色 (RBAC)、基于具体权限 |
| 例子 | 输对密码登录系统 | 只有"经理"角色才能查看工资单 |


2. 常见的使用场景

  1. 传统的 Web 应用:使用 Session + Cookie 维持登录状态。

  2. 前后端分离项目 :结合 JWT ( JSON Web Token ),通过拦截器或过滤器校验 Token,实现无状态认证。

  3. 微服务安全 :配合 OAuth2.0OIDC,实现分布式系统的统一身份认证(单点登录)。

四、示例

我们回到目前企业级开发中最主流、最稳健的版本:Spring Boot 3.x + Spring Security 6.x

这个版本是目前大多数公司新项目的首选,也是面试中最常被问到的版本。


1.环境准备 (Maven)

pom.xml 中引入依赖。Spring Boot 3.x 默认集成的就是 Spring Security 6.x。

复制代码
<dependencies>
    <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>
</dependencies>

2.核心配置类:SecurityFilterChain

在 6.x 版本中,官方已经完全废弃WebSecurityConfigurerAdapter。现在的标准写法是向 Spring 容器注入一个 SecurityFilterChainBeanSpring Security

java 复制代码
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.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 1. 禁用 CSRF(如果是前后端分离项目,通常关闭)
            .csrf(csrf -> csrf.disable())
            
            // 2. 配置请求授权
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**", "/public/**").permitAll() // 所有人可访问
                .requestMatchers("/admin/**").hasRole("ADMIN")        // 需 ADMIN 角色
                .anyRequest().authenticated()                         // 其他所有请求需登录
            )
            
            // 3. 开启表单登录(Spring Security 自带的登录页)
            .formLogin(form -> form
                .defaultSuccessUrl("/home") // 登录成功后的跳转地址
                .permitAll()
            )
            
            // 4. 开启 HTTP Basic 认证(可选,用于 API 测试)
            .httpBasic(Customizer.withDefaults());

        return http.build();
    }
}

3.用户与密码配置

你需要配置用户数据源和加密方式。在 Spring Security 6.x 中,密码必须加密,否则会报错。

java 复制代码
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class UserDetailConfig {

    // 配置密码加密器:使用 BCrypt 强哈希算法
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 配置用户数据源:这里先用内存模式演示
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.builder()
                .username("yufeng")
                .password(passwordEncoder().encode("123456")) // 密码加密存储
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("admin123"))
                .roles("ADMIN", "USER")
                .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

4.编写接口测试

创建一个简单的 Controller 来验证权限控制。

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/public/test")
    public String publicApi() {
        return "公开接口,无需登录";
    }

    @GetMapping("/user/info")
    public String userApi() {
        return "普通用户接口,登录后可见";
    }

    @GetMapping("/admin/manage")
    public String adminApi() {
        return "管理员接口,仅 ADMIN 角色可见";
    }
}

5.核心逻辑拆解

  1. 认证拦截 :当你访问 /user/info 时,AuthorizationFilter 发现你未登录,会触发 ExceptionTranslationFilter 重定向到登录页。

  2. 密码校验 :登录时,DaoAuthenticationProvider 会调用 UserDetailsService 拿用户信息,并用 PasswordEncoder 比对你输入的密码。

  3. 角色前缀 :在代码里写 .hasRole("ADMIN") 时,Spring Security 默认会自动在前面补上 ROLE_。所以数据库或内存里存储的权限名应该是 ROLE_ADMIN


五、WebSecurityConfigurerAdapter VS SecurityFilterChain

1.WebSecurityConfigurerAdapter (旧时代的基石)

在 Spring Security 5.7 之前,这是所有开发者绕不开的基类。

  • 实现方式基于 继承 (Inheritance-based) 。你需要创建一个类继承它,并重写 configure(HttpSecurity http) 方法。

  • 设计模式:它是典型的"模板方法模式"。父类定义了流程,子类填充细节。

  • 局限性

    • 耦合:你的配置类必须死死地绑定在 Spring Security 的特定基类上。

    • 灵活性差:如果你想在同一个应用中配置多个逻辑完全不同的过滤器链,继承模式会变得非常臃肿和难以维护。

    • 不符合 Spring Boot 理念 :Spring Boot 核心理念是"组合优于继承",倾向于通过 @Bean 注入来配置组件。


2.SecurityFilterChain Bean (现代标准)

这是从 Spring Security 5.7 开始引入,并在 6.x 中成为唯一标准的写法。

  • 实现方式基于组件 (Component-based) 。你不再继承任何类,而是向 Spring 容器注入一个类型为 SecurityFilterChain@Bean

  • 核心逻辑

java 复制代码
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults());

    return http.build(); // 关键:手动构建并返回对象
}
  • 优势

    • 解耦:你的配置类是一个纯粹的 Spring 配置类,不再依赖复杂的继承体系。

    • 多链 并行 :你可以非常轻松地定义多个 SecurityFilterChain Bean。例如,一个用于 /api/**(基于 JWT),另一个用于 /web/**(基于 Session),只需通过 @Order 注解指定优先级即可。

    • 测试友好:由于它是独立的 Bean,在进行单元测试或集成测试时,Mock 和替换变得更加简单。


3.核心对比表

|-----------|----------------------------------|------------------------------|
| 特性 | WebSecurityConfigurerAdapter (旧) | SecurityFilterChain Bean (新) |
| 配置风格 | 继承 (extends) | Bean 注入 (@Bean) |
| 构建方式 | 框架自动调用 configure 方法 | 开发者调用 http.build() 手动返回 |
| 版本状态 | 已彻底移除 (6.x) | 当前标准实现 |
| Lambda 支持 | 支持,但不强制 | 强制推荐,写法更现代 |
| 多配置处理 | 需定义多个内部静态类继承 Adapter | 定义多个 @Bean 配合 @Order 即可 |


4.架构图解:它是如何工作的?

在 6.x 架构中,SecurityFilterChain 本质上是一个包含了一组 Filter 的列表。当请求进来时:

  1. DelegatingFilterProxy 接收请求。

  2. 将其委托给 FilterChainProxy

  3. FilterChainProxy 会查看你定义的所有的 SecurityFilterChain Bean。

  4. 根据路径匹配(RequestMatcher)找到最合适的那条"链",然后让请求依次通过链上的过滤器。

关于作者 : 💡 予枫 ,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:

https://cloud.tencent.com/developer/support-plan?invite_code=9wrxwtlju1l

当前加入还有惊喜相送!

相关推荐
D_FW2 小时前
【Java】SpringAMQP+RabbitMQ消息可靠性保证
java·rabbitmq·java-rabbitmq
区区一散修2 小时前
0.IntelliJ IDEA的安装和使用
java·ide·intellij-idea
这周也會开心2 小时前
多线程与并发-知识总结1
java·多线程·并发
野犬寒鸦2 小时前
从零起步学习RabbitMQ || 第二章:RabbitMQ 深入理解概念 Producer、Consumer、Exchange、Queue 与企业实战案例
java·服务器·数据库·分布式·后端·rabbitmq
计算机毕设指导62 小时前
基于微信小程序的驾校预约管理系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
Seven972 小时前
剑指offer-64、滑动窗⼝的最⼤值
java
进击的小菜鸡dd2 小时前
互联网大厂Java面试:微服务、电商场景下的全栈技术问答与解析
java·spring boot·缓存·微服务·消息队列·日志·电商
星河耀银海2 小时前
C++基础数据类型与变量管理:内存安全与高效代码的基石
java·开发语言·c++
sunnyday04262 小时前
Spring Boot 应用启动成功后的事件监听与日志输出实践
java·spring boot·后端