Spring Security 示例——实现最简单的身份认证
首先,我们会编写一个只有最基本功能的 Spring Security 程序,只有最基本的最简单的身份验证,之后我们会随着学习不断完善这个项目
Spring Security 接入
引入依赖,我们引入 Spring Security 对应的starter
1 | <dependency> |
引入依赖后,如果你直接就开始写,在尝试去访问接下来写的接口就会自动跳转到一个 Spring Security 的默认登陆页面,默认用户名是 user, 密码会输出在控制台。
须登陆之后才能对接口进行访问。
创建Controller
控制器也没什么好说的,还是编写接口,只不过要注意,对于需要访问权限才能访问的接口,需要获取其登录的身份信息并且进行校验
1 | package hbnu.project.securitydemo.example.controller; |
编写配置类
在这个其中,我们会配置一些安全过滤链和建立两个内存用户存储作为基础用户,要不然每次都要从控制台获取密码,蛮麻烦的,过滤器链如何配置我们后面会专门讲Spring Security 自定义配置
1 | package hbnu.project.securitydemo.example.config; |
启动测试
启动类没什么不一样的,忽略了,直接启动项目
对了我前端写的是thy页面引擎,使用动态链接可以会做自动的对应处理,会自动处理相对路径

不知道为什么 css 没有给我加载进来,我开一下梯子试试
Spring Security 默认做了什么
我们几乎什么都没有做,为什么我们的应用就被保护起来了
Spring Security 在集成到 Spring Boot 应用后,会默认提供一系列 “开箱即用” 的安全功能,这些默认配置无需开发者手动编写代码,就能为应用提供基础安全防护。
Spring Security 的默认配置为应用提供了 “零配置启动” 的基础安全保障,包括用户认证、资源授权、常见攻击防护等核心功能。
默认的认证机制
生成默认用户
- 自动创建一个用户名为
user
的默认用户,密码会在应用启动时随机生成并打印在控制台(格式:Using generated security password: xxxxxxxxxx
)。 - 该用户默认拥有
ROLE_USER
角色,可访问所有受保护资源。
- 自动创建一个用户名为
表单登录
- 提供默认的登录页面(路径为
/login
),包含用户名、密码输入框和登录按钮。 - 登录请求默认提交到
/login
(POST 方法),登录失败会跳转至/login?error
,并显示错误信息。 - 登录成功后,默认重定向到用户最初请求的受保护页面(若直接访问登录页,则重定向到
/
)。
- 提供默认的登录页面(路径为
注销功能
- 默认支持注销,请求路径为
/logout
(GET 方法),注销后会销毁当前会话并跳转至/login?logout
。
- 默认支持注销,请求路径为
默认的授权规则
- 资源访问控制
- 所有 HTTP 请求(除登录页
/login
、注销页/logout
、静态资源等)均需要认证后才能访问。 - 公开路径默认包含:
/login
(登录页)、/login?error
(登录失败页)、/logout
(注销页)、/webjars/**
(WebJar 静态资源)、/css/**
、/js/**
等静态资源路径。
- 所有 HTTP 请求(除登录页
- 角色与权限
- 默认用户仅拥有
ROLE_USER
角色,无其他特殊权限。 - 授权判断默认区分角色前缀(
ROLE_
),例如hasRole("USER")
实际匹配ROLE_USER
权限。
- 默认用户仅拥有
默认安全防护
- 密码加密
- 默认使用
BCryptPasswordEncoder
对密码进行加密存储(不允许明文密码),即使是默认生成的用户,其密码也是加密后的。
- 默认使用
- CSRF 防护
- 默认启用 CSRF(跨站请求伪造)防护,会为所有非 GET 请求生成 CSRF 令牌(存储在 Session 中),并要求请求携带该令牌(通常通过表单隐藏字段或请求头传递)。
- 若请求未携带有效 CSRF 令牌,会被拒绝(返回 403 Forbidden)。
- 会话管理
- 默认创建 HTTP
会话(
SessionCreationPolicy.IF_REQUIRED
),用于存储用户认证信息。 - 登录时自动更换会话 ID(防止会话固定攻击)。
- 会话过期时间默认由 Servlet 容器决定(通常为 30 分钟)。
- 默认创建 HTTP
会话(
- 安全响应头
- 自动为响应添加安全相关的 HTTP 头,例如:
X-Content-Type-Options: nosniff
(防止 MIME 类型嗅探)X-Frame-Options: DENY
(防止点击劫持)X-XSS-Protection: 1; mode=block
(启用浏览器 XSS 过滤)
- 自动为响应添加安全相关的 HTTP 头,例如:
默认集成与适配
- Spring Boot 自动配置
- 无需手动注册过滤器链,Spring Boot 会自动创建
SecurityFilterChain
等核心组件。 - 若引入
spring-boot-starter-web
,默认适配 Servlet 环境;若引入spring-boot-starter-webflux
,则适配 Reactive 环境。
- 无需手动注册过滤器链,Spring Boot 会自动创建
- 与其他组件集成
- 若应用使用 Thymeleaf,默认支持
sec:
命名空间(如<sec:authorize access="isAuthenticated()">
),可在页面中直接判断用户权限。 - 与 Spring Session 集成时,默认支持分布式会话(需额外依赖)。
- 若应用使用 Thymeleaf,默认支持
简单说 Spring Security 的底层原理
回顾过滤器
Servlet 过滤器(Filter)和过滤器链(FilterChain) 的工作原理与 Spring Security 的工作息息相关,这是理解 Spring Security 核心机制的基础,因为 Spring Security 就是通过过滤器链来实现安全拦截的。
过滤器链(FilterChain)的结构如下
首先看示意图:
- 客户端(Client)发送 HTTP 请求到服务器。
- 服务器会创建一个
FilterChain
(过滤器链),链中包含多个Filter
(过滤器)和最终的Servlet
(如 Spring MVC 中的DispatcherServlet
)。 - 请求会按顺序经过
Filter₀
→Filter₁
→Filter₂
→ … →Servlet
,响应则按相反顺序返回(Servlet
→Filter₂
→Filter₁
→Filter₀
→ 客户端)。

过滤器是请求处理的
“拦截器”,那么在正常情况下,在程序启动的时候就要被注册到对应的
Servlet 容器中才能工作,然后过滤器可以在请求到达 Servlet
之前(或响应返回客户端之前)做一些操作,采用的是 Spring
Boot的形式,配置更是采用 Spring MVC 的形式,核心能力有两个:
拦截请求 / 响应:如果过滤器决定 “阻止” 请求,就可以直接向客户端返回响应,下游的过滤器和
Servlet
不会被调用。比如:Spring Security 的 “认证过滤器” 如果发现请求没有携带合法凭证,会直接返回 “401 未认证” 响应,不会让请求到达业务 Servlet
修改请求 / 响应:过滤器可以修改
HttpServletRequest
(如添加请求头、修改参数)或HttpServletResponse
(如添加响应头、修改返回内容),再传递给下游组件。
过滤器的执行逻辑可以抽象成如下
1 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { |
chain.doFilter(...)
这行代码决定了
“是否让请求继续向下游传递”。如果不调用它,下游的过滤器和
Servlet
就不会执行(相当于拦截了请求)。
因为每个过滤器只影响 “下游” 的组件,所以过滤器在链中的顺序非常关键。比如:“认证过滤器” 必须在 “授权过滤器” 之前执行 —— 先验证用户身份,再判断用户权限。
那么过滤器链和 Spring Security 的关系在上面说了,Spring Security 就是基于这个机制工作的,那么是如何体现的
- 它会向
FilterChain
中添加一系列安全相关的过滤器(比如UsernamePasswordAuthenticationFilter
处理表单登录、CsrfFilter
处理 CSRF 防护等)。 - 这些过滤器按特定顺序执行,依次完成 “认证 → 授权 → 安全防护”
等操作,最终才会把合法请求交给业务
Servlet
(如 Spring MVC 的DispatcherServlet
)。
过滤器链是 Servlet 规范的核心机制,Spring Security 借助它实现了 “无侵入式” 的安全拦截。我们之后会看 Spring Security 每个过滤器链以及其中的过滤器的
但如果 Filter 作为一个 spring 容器中 bean 对象管理呢?那么启用,禁用过滤器的操作就会更加灵活。那么,Spring Security 是如何处理这个问题的呢?
过滤链代理和委托过滤器代理
Spring Security 中 FilterChainProxy
和
DelegatingFilterProxy
的角色相当重要,它们是
Spring Security 实现 “安全过滤器链管理” 的核心机制。
首先,FilterChainProxy
就是Spring Security 的
“过滤器链代理”
- 它是 Spring Security 提供的特殊
Filter
,本身不直接做安全逻辑,而是代理到多个SecurityFilterChain
(安全过滤器链),实现调用了 Spring Security 自己的过滤器链,发挥安全框架的作用。 - 每个
SecurityFilterChain
包含一系列 Spring Security 专属的过滤器(如认证过滤器、授权过滤器、CSRF 过滤器等),负责具体的安全操作。 - 它通过代理模式,灵活管理多组安全过滤器链(比如不同的 URL
路径可以对应不同的
SecurityFilterChain
)。
这样就算是实现了上面我们说的灵活管理 Filter 了
那么DelegatingFilterProxy
是干什么的?
- 它是“委托过滤器代理”,是 Servlet 规范的
Filter
,但作用是桥接 Spring 容器和 Servlet 容器。 - 因为
FilterChainProxy
是 Spring 容器中的 Bean(由 Spring 管理生命周期),而 Servlet 容器(如 Tomcat)不直接认识 Spring Bean。而DelegatingFilterProxy
的作用就是会从 Spring 容器中找到FilterChainProxy
Bean,并将请求委托给它。
他们的关系可以看下图

上述示意图的请求流向可以剖析成如下内容:
- 客户端发送请求 → 经过通用过滤器
Filter₀
→ 到达DelegatingFilterProxy
。 DelegatingFilterProxy
从 Spring 容器中找到FilterChainProxy
Bean,把请求委托给它。FilterChainProxy
根据请求的 URL 等条件,选择对应的SecurityFilterChain
(一组 Spring Security 过滤器)。- 请求经过
SecurityFilterChain
中的一系列安全过滤器(完成认证、授权、CSRF 防护等)→ 再回到FilterChainProxy
→ 继续经过通用过滤器Filter₂
→ 最终到达业务Servlet
(如 Spring MVC 的DispatcherServlet
)。
那么为什么要这两层代理
DelegatingFilterProxy
:解决 “Spring Bean 与 Servlet
容器的桥接”
- Servlet 容器(如 Tomcat)启动时,会初始化自己的过滤器链,这些过滤器是独立于 Spring 容器的。
- 但
FilterChainProxy
是 Spring 容器中的 Bean(由@Bean
或配置类定义),Servlet 容器无法直接调用 Spring Bean。 DelegatingFilterProxy
作为 “中间人”,让 Servlet 容器的过滤器能调用到 Spring 管理的FilterChainProxy
。
FilterChainProxy
:解决 “多组安全过滤器链的灵活管理”
- 实际场景中,不同的请求可能需要不同的安全规则(比如
/api/**
路径需要 JWT 认证,/admin/**
路径需要会话认证 + 角色校验)。 FilterChainProxy
可以根据请求匹配规则(如 URL 模式),动态选择对应的SecurityFilterChain
,从而实现 “不同路径走不同的安全流程”。
之前的 “普通过滤器链” 是 Servlet 规范的原生链(Filter₀
→
Filter₁
→ Servlet
),而 Spring Security 通过
DelegatingFilterProxy
和
FilterChainProxy
,在原生链中插入了 “Spring
管理的安全过滤器链”,从而实现 “非侵入式” 的安全增强。
这两层代理就是 Spring Security 的 架构设计亮点 了:
DelegatingFilterProxy
负责桥接 Spring 和 Servlet 容器,让 Spring 管理的 Bean 能参与 Servlet 过滤器链。FilterChainProxy
负责管理多组安全过滤器链,让不同请求可以走不同的安全逻辑。
安全过滤链SecurityFilterChain
SecurityFilterChain
就是安全过滤器链,它是 Spring Security 专属的
“过滤器链单元”,它的核心使命是:
- 封装一组特定的安全过滤器(如认证过滤器、授权过滤器、CSRF 过滤器等)。
- 由
FilterChainProxy
决定 “当前请求应该使用哪一组SecurityFilterChain
”,从而实现不同请求走不同安全逻辑。
我们来看单 SecurityFilterChain
的流程

看上图的流程:
- 客户端请求 → 经过通用过滤器
Filter₀
→ 到达DelegatingFilterProxy
。 DelegatingFilterProxy
委托给FilterChainProxy
。FilterChainProxy
选择对应的SecurityFilterChain
→ 请求经过该链中的Security Filter₀
→ … →Security Filterₙ
→ 再回到FilterChainProxy
→ 继续经过通用过滤器Filter₂
→ 最终到Servlet
。
SecurityFilterChain
内部的 “安全过滤器” 是 Spring
Security 实现具体安全功能的载体(比如
UsernamePasswordAuthenticationFilter
处理表单登录,CsrfFilter
处理 CSRF 防护)。
下图展示了 “多个 SecurityFilterChain
共存时,如何匹配请求” 的核心逻辑,这是 Spring Security
灵活性的体现:

每个 SecurityFilterChain
都可以绑定特定的请求匹配规则(如 URL
模式、请求方法、请求头特征等)。
按照图中的示例来的话就是
SecurityFilterChain₀
:匹配 URL 为/api/**
的请求。SecurityFilterChainₙ
:匹配 URL 为/**
的请求(所有请求,作为 “兜底” 规则)。
FilterChainProxy
会按配置顺序遍历所有
SecurityFilterChain
,找到第一个匹配的链,然后只执行该链的过滤器(后续链不再执行)。只有第一个匹配的
SecurityFilterChain
被调用。
那么按照图上的示例,就可以这么理解
- 请求 URL 为
/api/messages/
:- 先匹配
SecurityFilterChain₀
(/api/**
)→ 执行该链的过滤器,不会再匹配后续的SecurityFilterChainₙ
(即使它也能匹配/**
)。
- 先匹配
- 请求 URL 为
/messages/
:- 不匹配
SecurityFilterChain₀
(/api/**
)→ 继续匹配SecurityFilterChainₙ
(/**
)→ 执行该链的过滤器。
- 不匹配
这样就实现了 Spring Security
过滤器链的自定义基础,因为SecurityFilterChain
是可以自定义的:
- 每个
SecurityFilterChain
内部的过滤器数量、类型都可以完全自定义(比如SecurityFilterChain₀
只需 3 个过滤器,SecurityFilterChainₙ
需 4 个)。 - 甚至可以配置 “零过滤器” 的
SecurityFilterChain
:用于让 Spring Security 完全忽略某些请求(比如静态资源路径,无需任何安全拦截)。
SecurityFilterChain
是 Spring Security 实现
“动态安全规则” 的核心载体:
- 它封装了一组特定的安全过滤器,负责具体的安全逻辑。
- 多个
SecurityFilterChain
可以通过优先级匹配,为不同请求提供定制化的安全防护。
理解这一点,就能明白 Spring Security 如何灵活应对复杂的业务场景(比如同时支持 “普通用户会话认证” 和 “第三方系统 JWT 认证”)。
从 DefaultSecurityFilterChain 再看默认配置
DefaultSecurityFilterChain
类是 Spring Security
中用于构建安全过滤器链的一个重要类,它实现了
SecurityFilterChain
、BeanNameAware
和
BeanFactoryAware
接口。
它的作用就是将 “请求匹配规则” 和 “一组安全过滤器” 绑定在一起,让特定的请求只能通过对应的安全过滤器处理。实现 “什么样的请求,用什么样的安全过滤器处理”
这个类是 SecurityFilterChain
中的其中一个过滤器链,也就是DefaultSecurityFilterChain
是
SecurityFilterChain
的一个实现类。Spring Security
的整个安全机制,就是由一个或多个 DefaultSecurityFilterChain
组成的。
- 默认配置中,Spring Security 会自动创建一个
“全局过滤器链”(
RequestMatcher
为/**
,匹配所有请求),包含 10+ 个核心过滤器(如认证、授权、CSRF 等)。实现了Spring Security 的默认行为。 - 开发者可以自定义多个
DefaultSecurityFilterChain
,实现 “不同路径不同安全规则”(比如/api
用 JWT 过滤器,/admin
用会话过滤器)。
然后所有 DefaultSecurityFilterChain
会被
FilterChainProxy
(安全过滤器链的
“调度中心”)管理。对应了上面说的当请求到达时,FilterChainProxy
会按顺序检查每个 DefaultSecurityFilterChain
的
matches()
方法,找到第一个匹配的链,然后执行该链中的所有过滤器。

其中,filters
的集合,存储了该安全过滤器链中包含的所有
Filter
实例,这些过滤器会按照顺序依次对请求进行处理。requestMatcher
是一个
RequestMatcher
类型的对象,用于判断当前的
HttpServletRequest
是否匹配该过滤器链。也就是说,它决定了什么样的请求会进入到这个过滤器链中进行处理。
有两个构造函数,主要功能是初始化 requestMatcher
和
filters
。第二个构造函数是实际执行初始化逻辑的,第一个构造函数只是做了一个参数转换,将可变参数
Filter... filters
转换为 List<Filter>
再调用第二个构造函数。
在第二个构造函数中,会先判断 filters
是否为空,如果为空,记录日志表示不会对匹配该 requestMatcher
的请求进行安全处理;如果不为空,记录日志说明会使用哪些过滤器对匹配的请求进行安全处理。
getRequestMatcher
方法
1 | public RequestMatcher getRequestMatcher() { |
该方法用于获取
requestMatcher
,RequestMatcher
决定
“管哪些请求”,外部可以通过这个方法得知当前过滤器链是根据什么规则来匹配请求的。
getFilters
方法
1 | public List<Filter> getFilters() { |
用于获取当前过滤器链中包含的所有过滤器,方便对过滤器链的组成进行查看和分析。其中,List<Filter>
:决定
“怎么处理请求”
matches
方法
1 | public boolean matches(HttpServletRequest request) { |
该方法用于判断传入的 HttpServletRequest
是否匹配当前过滤器链。实际上就是调用了 requestMatcher
的
matches
方法,根据其内部定义的匹配规则(比如基于 URL
路径、请求方法等)来判断请求是否符合进入该过滤器链的条件。
toString
方法就不说了
其中有个 setBeanName 方法,就是设置该 bean 在 Spring 容器中的名称,把过滤器链当成 bean 管理
1 | public void setBeanName( { String name) |
最后举个例子理解这个类
当你引入 Spring Security 依赖后,默认会创建一个
DefaultSecurityFilterChain
:
RequestMatcher
:anyRequest()
(匹配所有请求)。filters
:包含以下关键过滤器(简化版):CsrfFilter
:处理 CSRF 防护。UsernamePasswordAuthenticationFilter
:处理表单登录。FilterSecurityInterceptor
:最终的授权判断(检查用户是否有权限访问资源)。
当你访问 /admin
时:
- 请求被
DelegatingFilterProxy
拦截,交给FilterChainProxy
。 FilterChainProxy
发现默认的DefaultSecurityFilterChain
匹配所有请求,于是执行它的过滤器链。- 依次经过 CSRF 检查 → 登录状态检查(未登录则跳转登录页)→
授权检查(判断是否有
ADMIN
角色)→ 最终允许 / 拒绝访问。
我们可以总结 Spring Security
为开发者提供了一整套基于过滤器链(Filter
Chain)的安全防护机制。从用户发起 HTTP
请求,到经过多个安全过滤器的逐层检查,直至最终认证与授权完成。
我们来看一下 Spring Security
默认
DefaultSecurityFilterChain
启动的时候,默认加载的
16 个过滤器

Spring Security 已经帮我们封装了大部分的过滤器,实际上我们主要关注以下两个方面即可:
- 身份认证流程:如何通过表单登录(或其他方式)实现用户身份验证,及其内部如何利用 AuthenticationManager、ProviderManager 和多个 AuthenticationProvider 协同工作
- 授权机制解析:如何通过安全拦截器(如
AbstractSecurityInterceptor
)实现方法级别和 URL 级别的权限控制。
从 SecurityProperties 再看默认配置
其中,Spring Security 启动涉及到一个重要的类就是
SecurityProperties,是 Spring Boot 自动配置 Spring Security
时的核心配置属性类,它的作用是将外部配置(如
application.properties
/yml
)与 Spring Security
的默认行为关联起来,让开发者可以通过简单的配置项自定义 Security
的基础行为。
该类使用 @ConfigurationProperties("spring.security")
注解,说明它会读取配置文件中以 spring.security
为前缀的配置项,并映射到类的字段中。
例如,开发环境中只需一行配置
spring.security.user.password=123
,就能用简单密码登录,无需复杂的用户配置;生产环境中再替换为自定义的
UserDetailsService
,实现从数据库读取用户。

这个配置类的核心结构包含两个内部静态类:
Filter
:配置 Spring Security 过滤器的相关属性(如过滤器顺序、拦截 的请求类型)。User
:配置默认用户的相关属性(如用户名、密码、角色)。
先来看 User 内部类,它控制默认用户的生成

Spring Security 默认会创建一个用户用于登录,而 User
内部类就是这个默认用户的 “配置源”,核心字段:
name
:默认用户名为user
,可通过spring.security.user.name
修改。password
:默认是随机 UUID(启动时打印在控制台),若通过spring.security.user.password
手动设置,则使用自定义密码。roles
:默认是空集合,可通过spring.security.user.roles
配置角色(如ADMIN
)。passwordGenerated
:标记密码是否是自动生成的(若手动设置密码,则为false
)。
Spring Boot 自动配置会读取 User
中的属性,创建一个内存用户(InMemoryUserDetailsManager
),作为默认的认证用户。如果开发者没有自定义
UserDetailsService
,这个默认用户就会生效。
再看 Filter 内部类,它控制 Security 过滤器的行为

我们上面一直在说 Spring Security
的核心逻辑通过过滤器链实现,其中的Filter
内部类用于配置这个过滤器链的基础属性
order
:过滤器的执行顺序,默认值为-100
。Spring 中过滤器的
order
越小,执行越早。Security 过滤器需要在业务过滤器(如DispatcherServlet
)之前执行,因此默认值为负数。可通过
spring.security.filter.order
调整顺序dispatcherTypes
:指定过滤器拦截的请求类型,默认是EnumSet.allOf(DispatcherType.class)
(拦截所有类型的请求)。DispatcherType
包括REQUEST
(普通请求)、FORWARD
(转发)、INCLUDE
(包含)、ERROR
(错误页)等。例如,若只想拦截普通请求,可配置为[REQUEST]
。
Spring Boot 会根据 Filter
中的配置,初始化
DelegatingFilterProxy
(前文提到的过滤器代理),并设置其执行顺序和拦截的请求类型,确保
Security 过滤器链能正确介入请求处理流程。
这样,这个类就实现了提供默认配置,实现
“零配置启动”的根本作用,当开发者引入
spring-boot-starter-security
但未做任何配置时,SecurityProperties
会提供一套默认值:
- 默认用户
user
+ 随机密码,确保应用至少有一个可登录用户。 - 过滤器按默认顺序执行,拦截所有请求,确保安全逻辑生效。
而且还允许开发者通过配置文件自定义基础行为,也算是 Spring Security 最基础的自动配置内容了
Spring Security 的核心架构
在
Spring Security
中,整个安全流程围绕着一个核心组件展开:FilterChainProxy。它负责拦截所有
HTTP
请求,并将其传递给预先配置好的多个安全过滤器。这些过滤器中,最关键的包括:
UsernamePasswordAuthenticationFilter
:处理基于表单提交的登录请求。BasicAuthenticationFilter
:处理 HTTP Basic 认证请求。FilterSecurityInterceptor
:负责权限授权,调用AccessDecisionManager
来判定当前用户是否有权访问目标资源。

具体的身份认证流程和授权在后面讲解