
大家好我是你们的老朋友。之前陆续写过不少框架整合、微服务、数据库相关的实战文章经常有读者问能不能出一套完整的安全认证授权教程从最简单的入门 Demo 开始一直到分布式环境下怎么做登录、怎么做鉴权、怎么防串号一条线讲清楚。说实话这个主题一直想写但迟迟没动笔。不是因为没东西可写恰恰相反Spring Security 本身涉及的概念太多过滤器链、认证管理器、授权语义、会话并发、密码加密、CSRF、跨域、OAuth2、JWT……如果按官方文档那种严谨但枯燥的写法来新手大概率看完第三页就劝退了。所以这系列文章我会换一种讲法每集围绕一个真实场景展开先说明场景再给完整可运行的代码然后逐步分析配置背后的原理最后补充常见报错和线上避坑思路。这一篇是系列的第 0 集也就是前言和技术地图。我会先交代清楚这个系列到底讲什么、适合谁、需要准备什么环境以及整个系列的核心概念先做一个扫盲。同时把 Spring Security 6.x 时代的一些重要变化提前讲清楚避免后面代码示例中你突然看到一个不认识的写法。如果你正准备用 Spring Security 给自己的项目加上登录和权限控制或者面试前想在短时间内把 Spring Security 这块系统地过一遍那这篇文章可以作为你整个学习方向的第一站。1. 为什么要写这个系列Spring Security 学习痛点分析先聊几句题外话。Spring 家族里的框架很多Spring MVC、Spring Boot、Spring Cloud 都算容易上手照着文档复制粘贴就能跑通。但 Spring Security 是一个异类——官方文档写得及其抽象过滤器链的调用过程对于没接触过 Servlet 过滤器的同学来说就如同天书。我在网上看过不少关于 Spring Security 的学习资料质量参差不齐。有的教程还在用已经废弃的WebSecurityConfigurerAdapter有的教程把安全配置全部丢到一个类里然后就告诉你“会了”还有的教程上下文之间互相矛盾。当读者照着敲完代码发现自己的项目并没有出现想象中的登录页就会非常挫败。其实 Spring Security 本身的设计思路并不复杂它的核心可以概括为三句话所有请求在进入 Controller 之前必须经过一条过滤器链。过滤器链中的每个过滤器负责一件事认证、授权、CSRF 防护、跨域处理等。你想定制安全逻辑就是往这条链子上加组件或修改默认组件。真正让我们觉得难的原因不是概念复杂而是官方文档把这些概念拆得太散了。作为初学者你很难形成一张完整的图。所以这个系列我给自己定了一个目标不复制文档不逐行翻译源码而是按项目开发的真实路径把 Spring Security 拆成一集一集的实战课。每一集都从一个实际问题出发比如为什么我加了 security 依赖项目启动后访问接口就变成 401 了如何把我的用户表接入 Spring Security而不是用内存用户如何实现“用户登录后只能访问自己的数据”如何在前后端分离的项目中返回 JSON 而不是跳转登录页如何给接口动态配置权限比如通过数据库配置角色能访问的接口这些问题一个一个解决Spring Security 的基本全貌也就自然建立起来了。2. 本系列适合哪些读者我没有把读者限定为“零基础”因为零基础也分很多种。如果你完全没接触过 Java Web连 Servlet、Filter、DispatcherServlet 都没听说过那我不建议你直接从这个系列开始。建议先去把 Spring Boot 的基本使用过一遍能写一个简单的 REST 接口再回头看系列文章。如果你已经能独立写一些 Spring Boot 接口但从来没给项目加过登录认证逻辑或者说你之前一直用拦截器、Filter 手写 Token 校验现在想体系化地学习 Spring Security那这个系列真的非常合适。还有一类读者就是准备跳槽面试、需要梳理 Spring Security 源码和设计思想的技术候选人。系列文章中不只有代码也会有核心概念图、源码调用链分析和源码级排错思路这部分内容能够帮助你在面试中把“会用”升级为“理解”。总结一下适合阅读的人群会用 Spring Boot 写接口但没做过认证授权的开发者。手写过 Filter、拦截器做登录校验想对比学习 Spring Security 的开发者。项目中已经用到 Spring Security但业务一复杂就不知道怎么配置的开发者。面试前需要系统复习认证授权核心知识的求职者。如果你属于以上几类那这个系列应该能陪你走完从入门到项目中能独立落地的全过程。3. 环境准备与版本说明版本是一个很重要的问题。Spring Security 目前的主流版本是 5.7.x 和 6.x。其中 Spring Security 5.7 是旧系列的最后一个长期维护版本而 Spring Security 6.0 在 2022 年 11 月随 Spring Boot 3.0 一起发布带来了很多破坏性变更。在这个系列中我会以 Spring Security 6.x 作为主版本进行讲解原因很明确Spring Boot 3.x 已经发布多年新项目基本都用 3.x如果你还在用 Spring Boot 2.x也可以阅读系列中的大部分内容代码中需要留意lambda DSL和无WebSecurityConfigurerAdapter这两点新写法。具体环境信息列举如下JDK建议使用 JDK 17 或更高版本Spring Boot 3.x 强制要求 JDK 17。框架Spring Boot 3.x文中示例以 3.2.x 左右版本为例。构建工具Maven 3.8 或 Gradle 7.x 均可示例中统一使用 Maven。IDEIntelliJ IDEA 或 Eclipse推荐 IDEA演示效果更直观。数据库部分示例使用 MySQL 8.x并配合 MyBatis-Plus 或 Spring Data JPA 演示动态用户与动态权限。其他组件Redis可选用于分布式会话、Nacos可选用于注册中心与配置中心、JWT用于无状态认证。需要说明的是由于不同时间段各依赖版本会不断升级如果你在配置依赖时发现我的版本号和你的不一致不要慌优先参考你本地 Spring Boot Parent 管理的版本即可。多数情况下Spring Boot 的依赖管理已经帮你锁定了兼容的 Security 版本。示例项目结构我会采用多模块方式核心模块如下spring-security-tutorial/ ├── pom.xml ├── security-core/ # 核心认证授权封装 ├── security-01-basic/ # 第1集最简登录认证 ├── security-02-db/ # 第2集数据库用户认证 ├── security-03-jwt/ # 第3集前后端分离 JWT └── security-04-authorize # 第4集方法级权限与动态权限每个模块都可以独立运行方便你单独打开阅读对应集数的代码。4. 核心概念扫盲认证、授权与过滤器链正式代码开始之前先把三个核心概念讲清楚。4.1 认证Authentication认证回答的问题只有一个你是谁在 Spring Security 中认证的产物是一个Authentication对象。当用户未认证时Authentication对象中只有一个匿名用户标识或者为空当用户通过用户名密码等方式登录成功后Authentication对象中会包含用户的身份信息、凭证信息和权限列表。很多人容易混淆认证和登录。登录只是认证方式中的一种。认证可以支持用户名密码、手机验证码、微信扫码、OAuth2 第三方登录等多种形式。Spring Security 的AuthenticationManager是整个认证流程的核心入口它会根据请求中携带的凭证信息返回一个完整认证结果。4.2 授权Authorization授权回答的问题是你能做什么认证通过后系统需要判断当前用户有没有权限访问某个资源。Spring Security 中权限信息通常存储在Authentication对象里的GrantedAuthority集合中每一项就是一个权限标识。授权的实现层次很丰富在 URL 请求入口做过滤控制也就是基于HttpSecurity的请求规则配置。在方法调用层面做控制通过PreAuthorize、Secured等注解实现。在页面元素层面做控制通过sec:authorize标签实现。这个系列的重点会放在前两种因为实际项目中大多数接口权限都可以通过requestMatchers和PreAuthorize解决。4.3 过滤器链Filter ChainSpring Security 之所以强大核心就在于它集成了一个完整的过滤器链。在 Spring Boot 中Spring Security 会自动注册一条过滤器链所有请求都会在这个链路上经过一系列过滤器的处理。常见的过滤器包括UsernamePasswordAuthenticationFilter处理表单登录请求提取用户名密码。BasicAuthenticationFilter处理 HTTP Basic 认证。BearerTokenAuthenticationFilter处理携带 Token 的请求。AuthorizationFilter在认证通过后判断是否有权限访问对应资源。CsrfFilter处理 CSRF 防护。ExceptionTranslationFilter捕获认证和授权异常转换为标准响应。你可以通过HttpSecurity来调整这条过滤器链中的组件比如新增一个自定义过滤器、修改某个过滤器的执行位置、关闭某个默认过滤器等。很多同学在项目里遇到“请求被拦截了”“登录后依然 401”“跨域配置不生效”等问题本质都是对过滤器链的执行顺序不够理解。所以后面的系列中我会专门拿出一集来详细拆解过滤器链的调用过程。4.4 SecurityContext 与 SecurityContextHolder这里再补充一个高频考点。SecurityContext保存的是当前线程的认证信息。SecurityContextHolder是一个基于 ThreadLocal 的工具类默认情况下会在当前线程中存储SecurityContext。在普通的同步请求中你可以通过下面的方式获取当前登录用户Authentication authentication SecurityContextHolder.getContext().getAuthentication();不过在异步模式下、WebFlux 环境中或者子线程中SecurityContext的传递策略会发生变化后面讲到异步认证传播时再详细展开。5. 一个最简 Demo抢先感受 Spring Security 的默认行为在正式进入第 1 集之前先看一个最简示例帮助你建立对 Spring Security 的直观感受。这一节只做热身目标只有一个看到 Spring Security 的默认登录页。5.1 创建项目并添加依赖先创建一个空的 Spring Boot 项目添加两个依赖即可。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency /dependencies5.2 写一个测试接口创建启动类和 Controller。package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; SpringBootApplication RestController public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } GetMapping(/hello) public String hello() { return Hello Spring Security; } }5.3 启动并访问接口启动项目后在浏览器访问http://localhost:8080/hello你会发现你并没有直接看到接口返回的字符串而是在浏览器内被重定向到了一个默认的登录页http://localhost:8080/login这就是 Spring Security 的默认行为没有经过认证的请求不允许直接访问受保护资源。Spring Security 在默认情况下创建一个内存用户用户名为user密码是项目启动后在控制台打印出来的一段随机 UUID 字符串类似这样Using generated security password: 1e4f9d5f-5f0c-4b3f-8e9e-0d2cbf84cd29你可以在登录页输入user和这段随机密码登录成功后就会重定向回/hello接口看到返回结果。注意这只是默认行为。实际项目中没有人会用随机密码登录系统。我们需要做的是修改认证方式让 Spring Security 认识我们的用户。正是从这个小 Demo 开始后面的系列文章会一步一步把这个默认的登录流程改造成符合真实业务需要的认证授权流程。6. 常见问题见了就头疼的几个报错Spring Security 是一个报错信息极其晦涩的框架。很多报错一眼看上去根本不知道在说什么但其实本质原因就那么几个。这里先提前总结几个出现频率最高的后面的系列文章会逐个详细展开。问题现象常见原因解决思路启动报错Failed to configure a DataSource引入了需要数据源的依赖但没配置数据库连接检查是否需要数据库不需要则排除对应自动配置访问接口返回 401未登录访问受保护资源或认证方式不对检查请求是否携带了正确凭证或调整安全放行规则访问接口返回 403用户已认证但权限不足检查授权配置和当前用户权限列表表单登录后一直 404配置了登录认证处理器但路径设置错误确认loginProcessingUrl与登录页表单的 action 一致密码校验报错There is no PasswordEncoder mapped for the id null使用了{noop}之外的密码格式或未指定加密方式使用BCryptPasswordEncoder并确保密码存储格式匹配例如{bcrypt}前缀WebSecurityConfigurerAdapter报错或已废弃使用的是 Spring Security 5.7 以上或 6.x不要再继承该类使用组件式SecurityFilterChainBean 配置实际上一大半问题都出现在“版本新老混合”和“过滤器链配置顺序不当”上。由于 Spring Security 6 的配置方式相比老版本已经发生了较大改变很多网上的老教程代码已经没法直接使用。如果你使用的是 Spring Boot 3.x建议认真学习本系列中的新式写法不要再去看那些基于WebSecurityConfigurerAdapter的老代码。7. 本系列规划接下来每一集会讲什么目前这个系列的大致规划如下后续根据读者反馈可能会调整顺序第 1 集自定义用户名密码认证。不再使用默认随机密码把认证信息绑定到自定义用户上。第 2 集基于数据库实现用户认证。把用户表接入 Spring Security讲清楚UserDetailsService和UserDetails的作用解决“用户存在库里但无法登录”的问题。第 3 集密码加密与密码匹配机制。理解PasswordEncoder的设计对比BCrypt、SCrypt、Argon2等不同加密算法完成用户注册和密码校验。第 4 集请求级授权配置。深入讲解requestMatchers、permitAll、authenticated、hasRole、hasAuthority的区别与应用。第 5 集方法级安全。使用PreAuthorize等注解在 Service 层做更精细的权限控制解决“同是登录用户但权限不同”的问题。第 6 集前后端分离方案。让 Security 接口返回 JSON 而不是重定向处理 Ajax 请求的 401 与 403 响应格式。第 7 集JWT 登录认证。结合 Spring Security 实现无状态 Token 登录适合接口服务、APP 后端场景。第 8 集自定义过滤器。在过滤器链中插入自己的过滤器实现验证码校验、签名验签等扩展需求。第 9 集会话管理与并发控制。实现“单用户最大登录数”“踢人下线”等常见需求。第 10 集分布式场景下的认证方案。讨论 SSO 与 Redis 会话共享的基本思路。第 11 集源码级别分析。回顾过滤器链调用过程分析认证流程中各个组件的协作关系面试前可以重点看这一集。这个规划覆盖了从入门到进阶的完整路径。在阅读过程中官方文档可以用来做语法查询但这套系列可以帮你减少大量自己踩坑的时间。8. 学习建议和避坑指南这节想给准备跟着系列学习的朋友几条比较实在的建议。第一不要把 Spring Security 和 Spring Boot 割裂开。Spring Security 的很多自动化配置依赖 Spring Boot 的自动装配机制。同样一段 Security 配置在 Spring Boot 2 和 Spring Boot 3 下表现可能不一样。你需要做的不是死记硬背配置而是在理解自动配置的前提下去调整。第二不要只看不敲。Spring Security 的代码写得再多都不如你亲自动手运行一个带数据库的登录 Demo。建议准备一个空的 Spring Boot 工程在本地 MySQL 建一张sys_user表跟着系列文章把代码敲一遍。只有到了这一步才会真正理解UserDetailsService在干什么。第三遇到 401、403 报错不要慌。把当前请求的 URL、请求方式、是否携带 Token、后台日志里的异常类型四样信息列出来基本就能解决八成的问题。后续我会在每一集里附上常见报错对照表你可以直接按表排查。第四官方文档和源码是这个系列之外最有价值的参考。本系列文章用到的主要类建议你都在 IDE 里点进去看一眼源码注释比如SecurityFilterChain、UsernamePasswordAuthenticationFilter、UserDetailsService这几个核心类型。源码本身比任何二手资料都准确。9. 总结与下集预告在接下来的系列开篇中我们会动手完成第一件大事把 Spring Security 默认的随机用户改成我们自己配置的用户并讲清楚用户名密码在校验底层到底是怎么工作的。你会接触到几个在后面反复用到的重要类UserDetails、UserDetailsService、PasswordEncoder、AuthenticationManager等。这一集理解了这些概念后后面数据库版用户认证、JWT 登录等进阶内容理解起来就会非常顺。如果你对某个细节有疑问欢迎在评论区留言遇到频率高的问题我会在后续第 0.5 集或者其他补充内容中专门解答。下一集我们正式进入代码实战不见不散。