Spring Security入门
一、Spring Security介绍
1.简介
Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。由于它是Spring生态系统中的一员,因此它伴随着整个Spring生态系统不断修正、升级,在spring boot项目中加入Spring Security更是十分简单,使用Spring Security 减少了为企业系统安全控制编写大量重复代码的工作。
正如你可能知道的关于安全方面的两个核心功能是“认证”和“授权”,一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 SpringSecurity 重要核心功能。
- (1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录
- (2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。
1.1 竞品对比
Spring Security
Spring 技术栈的组成部分。
https://spring.io/projects/spring-security
通过提供完整可扩展的认证和授权支持保护你的应用程序
特点:
- 和 Spring 无缝整合
- 全面的权限控制
- 专门为 Web 开发而设计
- 旧版本不能脱离 Web 环境使用
- 新版本对整个框架进行了分层抽取,分成核心模块和 Web 模块。单独引入核心模块就可以脱离 Web 环境
- 重量级
Shiro
Apache 旗下的轻量级权限控制框架。
特点:
- 轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求的互联网应用有更好表现。
- 通用性。
- 好处:不局限于 Web 环境,可以脱离 Web 环境使用。
- 缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制
1.2 登录校验流程
二、Spring Security入门案例
要对Web资源进行保护,最好的办法莫过于Filter。
要想对方法调用进行保护,最好的办法莫过于AOP。Spring Security进行认证和鉴权的时候,就是利用的一系列的Filter来进行拦截的。
如图所示,一个请求想要访问到API就会从左到右经过蓝线框里的过滤器,其中绿色部分是负责认证的过滤器,蓝色部分是负责异常处理,橙色部分则是负责授权。进过一系列拦截最终访问到我们的API。
- FilterSecurityInterceptor:是一个方法级的权限过滤器,基本位于过滤器链的最底部。
- ExceptionTranslationFilter:是一个异常过滤器,用来处理认证授权过程中抛出的异常。
- UsernamePasswordAuthenticationFilter:对/login的POST请求做拦截,校验表单中用户名,密码。
这里面我们只需要重点关注两个过滤器即可:
UsernamePasswordAuthenticationFilter
负责登录认证,FilterSecurityInterceptor
负责权限授权。说明:Spring Security的核心逻辑全在这一套过滤器中,过滤器里会调用各种组件完成功能,掌握了这些过滤器和组件你就掌握了Spring Security!这个框架的使用方式就是对这些过滤器和组件进行扩展。
2.1引入依赖
<!-- Spring Security依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 说明:依赖包(spring-boot-starter-security)导入后,Spring Security就默认提供了许多功能将整个应用给保护了起来:
1、要求经过身份验证的用户才能与应用程序进行交互
2、创建好了默认登录表单
3、生成用户名为
user
的随机密码并打印在控制台上等等......
2.2添加配置类
@Configuration @EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { }
2.3启动测试
启动你自己的项目,并在浏览器输入任意接口路径,如:http://localhost:8080/test/findAll(填写你自己的接口)
会发现进入到了Spring Security默认提供认证页面,不需要额外开发,但实际中没人用。
默认账号:
user
密码随机生成在控制台:
55155fd5-5862-42d4-b508-e4d063467c8f
(每次都是随机不同的)
三、用户认证
3.1用户认证流程
3.2用户认证核心组件
我们系统中会有许多用户,确认当前是哪个用户正在使用我们系统就是登录认证的最终目的。这里我们就提取出了一个核心概念:当前登录用户/当前认证用户。整个系统安全都是围绕当前登录用户展开的,这个不难理解,要是当前登录用户都不能确认了,那A下了一个订单,下到了B的账户上这不就乱套了。这一概念在Spring Security中的体现就是 Authentication,它存储了认证信息,代表当前登录用户。
我们在程序中如何获取并使用它呢?我们需要通过 SecurityContext 来获取Authentication,SecurityContext就是我们的上下文对象!这个上下文对象则是交由 SecurityContextHolder 进行管理,你可以在程序任何地方使用它:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder
原理非常简单,就是使用ThreadLocal
来保证一个线程中传递同一个对象!现在我们已经知道了Spring Security的三个核心组件:
- Authentication:存储了认证信息,代表当前登录用户
- SeucirtyContext:上下文对象,用来获取Authentication
- SecurityContextHolder:上下文管理对象,用来在程序任何地方获取SecurityContext
Authentication中是什么信息呢:
Principal
:用户信息,没有认证时一般是用户名,认证后一般是用户对象
Credentials
:用户凭证,一般是密码
Authorities
:用户权限
3.3用户认证核心组件
Spring Security是怎么进行用户认证的呢?
AuthenticationManager
就是Spring Security用于执行身份验证的组件,只需要调用它的authenticate
方法即可完成认证。Spring Security默认的认证方式就是在UsernamePasswordAuthenticationFilter
这个过滤器中进行认证的,该过滤器负责认证逻辑。Spring Security用户认证关键代码如下:
// 生成一个包含账号密码的认证信息 Authentication authenticationToken = new UsernamePasswordAuthenticationToken(username, passwrod); // AuthenticationManager校验这个认证信息,返回一个已认证的Authentication Authentication authentication = authenticationManager.authenticate(authenticationToken); // 将返回的Authentication存到上下文中 SecurityContextHolder.getContext().setAuthentication(authentication);
下面我们来分析一下。
3.4认证接口分析
AuthenticationManager
的校验逻辑非常简单:根据用户名先查询出用户对象(没有查到则抛出异常)将用户对象的密码和传递过来的密码进行校验,密码不匹配则抛出异常。
这个逻辑没啥好说的,再简单不过了。重点是这里每一个步骤Spring Security都提供了组件:
是谁执行 根据用户名查询出用户对象 逻辑的呢?用户对象数据可以存在内存中、文件中、数据库中,你得确定好怎么查才行。这一部分就是交由
UserDetialsService
处理,该接口只有一个方法loadUserByUsername(String username)
,通过用户名查询用户对象,默认实现是在内存中查询。那查询出来的 用户对象 又是什么呢?每个系统中的用户对象数据都不尽相同,咱们需要确认我们的用户数据是啥样的才行。Spring Security中的用户数据则是由
UserDetails
来体现,该接口中提供了账号、密码等通用属性。对密码进行校验大家可能会觉得比较简单,
if、else
搞定,就没必要用什么组件了吧?但框架毕竟是框架考虑的比较周全,除了if、else
外还解决了密码加密的问题,这个组件就是PasswordEncoder
,负责密码加密与校验。我们可以看下
AuthenticationManager
校验逻辑的大概源码:
public Authentication authenticate(Authentication authentication) throws AuthenticationException { ...省略其他代码 // 传递过来的用户名 String username = authentication.getName(); // 调用UserDetailService的方法,通过用户名查询出用户对象UserDetail(查询不出来UserDetailService则会抛出异常) UserDetails userDetails = this.getUserDetailsService().loadUserByUsername(username); String presentedPassword = authentication.getCredentials().toString(); // 传递过来的密码 String password = authentication.getCredentials().toString(); // 使用密码解析器PasswordEncoder传递过来的密码是否和真实的用户密码匹配 if (!passwordEncoder.matches(password, userDetails.getPassword())) { // 密码错误则抛出异常 throw new BadCredentialsException("错误信息..."); } // 注意哦,这里返回的已认证Authentication,是将整个UserDetails放进去充当Principal UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities()); return result; ...省略其他代码 }
UserDetialsService
、UserDetails
、PasswordEncoder
,这三个组件Spring Security都有默认实现,这一般是满足不了我们的实际需求的,所以这里我们自己来实现这些组件!
---End---
永远积极向上!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了