# 基础:
Spring Security 是一个专注于为 Java 应用程序提供身份认证和授权的框架。推荐使用权限认证方式配置:SSM + Shiro;Springboot+SpringSecurity
- 身份认证(authentication),即验证用户身份的合法性,以判断用户能否登录。
- 授权(authorization),即验证用户是否有权限访问某些资源或者执行某些操作。
# 实质:
实质上 SpringSecurity 在进行身份认证方面主要通过一系列的过滤器链来实现的,我们加入 SpringSecurity 项目的时候可以看到控制台会输出 DefaultSecurityFilterChain 打印出来默认的过滤器链。如果我们想要对相应的地方做修改,只需修改过滤器即可,在过滤器链完成过程中加入我们的业务代码逻辑即可。
# SpringSecurity 核心过滤器链
我们看视频常会看到说有 15 个基本过滤器链 (Filter),但是我们常用的也就那几个。
# 浅要说一下:什么是过滤器链?
Filter 可以在服务器作出响应前拦截用户请求,并在拦截后修改 request 和 response,可实现一次编码、多处应用。Filter 主要有以下两点作用:
- 拦截请求:在 HttpServletRequest 到达 Servlet 之前进行拦截,查看和修改 HttpServletRequest 的 Header 和数据。
- 拦截响应:在 HttpServletResponse 到达客户端之前完成拦截,查看和修改 HttpServletResponse 的 Header 和数据。
过滤器链作为 SpringSecurity 的核心,我从网上找来一个图,可以很好的解释一下过滤器链的执行流程:
# SpringSecuity 部分过滤器的执行流程:
- SecurityContextPersistenceFilter:整个 Spring Security 过滤器链的开端。主要有两点作用:(1)当请求到来时,检查 Session 中是否存在 SecurityContext,若不存在,则创建一个新的 SecurityContext;(2)在请求结束时,将 SecurityContext 放入 Session 中,并清空 SecurityContextHolder。
- UsernamePasswordAuthenticationFilter:继承自抽象类 AbstractAuthenticationProcessingFilter。当进行表单登录时,该 Filter 将用户名和密码封装成 UsernamePasswordAuthenticationToken 进行验证。
- AnonymousAuthenticationFilter:匿名身份过滤器,一般用于匿名登录。当前面的 Filter 认证后依然没有用户信息时,该 Filter 会生成一个匿名身份 AnonymousAuthenticationToken。
- ExceptionTranslationFilter:异常转换过滤器,用于处理 FilterSecurityInterceptor 抛出的异常。但是只会处理两类异常:AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。
# 了解了 SpringSecurity 的执行流程之后,我们先来认识一下其中的核心组件:
- SecurityContextHolder:用于获取 SecurityContext 的静态工具类,是 Spring Security 存储身份验证者详细信息的位置。
- SecurityContext: 上下文对象,Authentication 对象会放在里面。
- Authentication: 认证接口,定义了认证对象的数据形式。
- AuthenticationManager: 用于校验 Authentication,返回一个认证完成后的 Authentication 对象。
我们可以随时获取 SecurityContext 上下文对象,这样我们可以更改其中的权限认证信息,这是很重要的,当时写社团在线平台就不知道这个想了好久。离大谱。
SecurityContextHolder 用于存储安全上下文(SecurityContext)的信息。而如何保证用户信息的安全,Spring Security 采用 “用户信息和线程绑定” 的策略,SecurityContextHolder 默认采用 ThreadLocal 机制保存用户的 SecurityContext,在使用中可以通过 SecurityContextHolder 工具轻松获取用户安全上下文。这意味着,只要是针对某个使用者的逻辑执行都是在同一个线程中进行,Spring Security 会在用户登录时自动绑定认证信息到当前线程,在用户退出时也会自动清除当前线程的认证信息。
其中,getAuthentication () 返回认证信息,getPrincipal () 返回身份信息。 SecurityContext 是从 SecurityContextHolder 获得的。SecurityContext 包含一个 Authentication 对象。
# SpringSecurity 的认证流程![]()
根据这张图,这样来看我的代码,就能很清晰了,我们需要实现 UserDetailsSerivce 的 loadUserByUsername () 方法,我们在这个方法里面进行查询数据库,判断用户是否存在来进行登录认证操作,同时我们的返回的 UserDetails 实体类是 Security 框架自带的,我们可以继承他,然后返回我们自己的实体类,注意返回的 UserDetails.
# 官方话如下:
结合上面的时序图,让我们先熟悉下 Spring Security 的认证流程:
- 用户进行认证,用户名和密码被 SecurityFilterChain 中的 UsernamePasswordAuthenticationFilter 过滤器拦截,并将请求封装为 Authentication,其默认实现类是 UsernamePasswordAuthenticationToken。
- 将封装的 UsernamePasswordAuthenticationToken 提交至 AuthenticationManager(认证管理器)进行认证。
- 认证成功后, AuthenticationManager(身份管理器)会返回一个包含用户身份信息的 Authentication 实例(包括身份信息,细节信息,但密码通常会被移除)。
- SecurityContextHolder (安全上下文容器)将认证成功的 Authentication 存储到 SecurityContext(安全上下文)中。
其中,AuthenticationManager 接口是认证相关的核心接口,ProviderManager 是它的实现类。因为 Spring Security 支持多种认证方式,所以 ProviderManager 维护着一个 List
列表,包含多种认证方式,最终实际的认证工作就是由列表中的 AuthenticationProvider 完成的。其中最常见的 web 表单认证的对应的 AuthenticationProvider 实现类为 DaoAuthenticationProvider,它的内部又维护着一个 UserDetailsService 负责获取 UserDetails。最终 AuthenticationProvider 将 UserDetails 填充至 Authentication。
# 用户密码过滤器(UsernamePasswordAuthenticationFilter):
以用户名密码认证为例 ,请求被 UsernamePasswordAuthenticationFilter 过滤器拦截,UsernamePasswordAuthenticationFilter 根据 Request 中提交的用户名和密码创建一个 Token (UsernamePasswordAuthenticationToken)。
# UsernamePasswordAuthenticationToken 这玩意是啥?
实质上:UsernamePasswordAuthenticationToken 的核心就是两个构造方法,分别用于初始化未认证和认证的 Token。
# 官方话:
这一步是身份认证的核心,下面进行详细讲解:
- 未认证的 UsernamePasswordAuthenticationToken(携带用户名、密码信息)被提交给 AuthenticationManager。AuthenticationManager 的实现类 ProviderManager 负责对认证请求链 AuthenticationProviders 进行管理。
- ProviderManager 通过循环的方式,发现 DaoAuthenticationProvider 的类型符合,使用 DaoAuthenticationProvider 进行认证。
- DaoAuthenticationProvider 从 UserDetailsService 中查找 UserDetails。
- DaoAuthenticationProvider 使用 PasswordEncoder 验证上一步返回的 UserDetails 中的用户密码。
- 当身份验证成功, Authentication 返回一个已认证的 UsernamePasswordAuthenticationToken ,其中包含 UserDetailsService 返回的 UserDetails 信息。最终,认证成功的 UsernamePasswordAuthenticationToken 添加到 SecurityContextHolder 完成账号密码的身份认证。
看下这图就了解差不多了:
