环境
SpringBoot版本:2.7.14
流程图
默认的Filter
SpringSecurity的默认Filter地址:http://t.csdn.cn/YH838
常见的认证授权技术
1、基于表单的认证(Cookie & Session)
基于表单的认证并不是在 HTTP 协议中定义的,而是服务器自己实现的认证方式,安全程度取决于实现程度。一般用 Cookie 来管理 Session 会话,是最常用的认证方式之一。它的安全程度取决于服务器的实现程度,客户端在Cookie中携带认证信息,服务器解析并返回结果。
2、基于JWT(Json Web Token)的认证
App和服务端常用的认证方式,用户ID和密码传输到服务器上验证,服务器验证通过以后生成加密的JWT Token返回给客户端,客户端再发起请求时携带返回的Token进行认证。(多了个防篡改)
3、Http Basic 认证
最早的 Http 认证方式,用户 ID 和密码以分号连接,经过 Base64 编码后存储到 Authorization 字段,发送到服务端进行认证 ;用户 ID/密码 以明文形式暴露在网络上,安全性较差。(如果没有使用 SSL/TLS 这样的传输层安全的协议,那么以明文传输的密钥和口令很容易被拦截)
4、Http Digest 认证
在 HttpBasic 的基础上,进行了一些安全性的改造,用户ID, 密码 , 服务器/客户端随机数,域,请求信息,经过 MD5 加密后存储到 Authorization 字段,发送到服务端进行认证;密码经过 MD5 加密,安全性比 Basic 略高。
5、其他认证方式(Oauth 认证,单点登陆,HMAC 认证)
通过特定的加密字段和加密流程,对客户端和服务端的信息进行加密生成认证字段,放在 Authorization 或者是消息体里来实现客户信息的认证。
引言
关于SpringSecurity启动是怎么加载、注册进Spring容器的;组件是怎么加载的可以看这两篇
SpringSecurity启动流程源码刨析地址:http://t.csdn.cn/vXbbD
SpringSecurity的请求分发执行流程源码刨析地址:http://t.csdn.cn/Yy1GB
认证流程概述
1、用户登录前,默认生成的Authentication对象处于未认证状态,登录时会交由Authentication Manager负责进行认证。
2、AuthenticationManager会将Authentication中的用户名/密码与UserDetails中的用户名/密码对比,完成认证工作,认证成功后会生成一个已认证状态的Authentication对象;
3、最后把认证通过的Authentication对象写入到SecurityContext中,在用户有后续请求时,可从Authentication中检查权限。
认证流程源码刨析
1、AbstractAuthenticationProcessingFilter
请求过来之后会经过一系列过滤器到这儿,这个过滤器是和认证授权直接相关的,它是个抽象的父类,内部定义了认证处理的过程。可以看到他在doFilter中调了attemptAuthentication方法;这个方法的实现是在UsernamePasswordAuthenticationFilter中。
2、UsernamePasswordAuthenticationFilter
跟进这个方法可以看到他的具体实现,进行了用户名、密码的校验;封装了UsernamePasswordAuthenticationToken对象;设置了details;
获取了AuthenticationManager对象,并且调用了authenticate方法进行认证;跟进此方法;
3、可以看到是进入了AuthenticationManager的实现类ProviderManager;调用了此实现类的authenticate方法;
4、通过循环遍历从providers中找到能支持此AuthenticationToken的AuthenticationProvider;如果找到就调用authenticate方法。如果找不到则调用父类的authenticate方法;
5、跟进authenticate方法;可以看到进入了AuthenticationProvider的实现类AbstractUserDetailsAuthenticationProvider;它在authenticate方法中判断了用户名是否在缓存中;如果不在会调用retrieveUser方法进行查询;
6、跟进retrieveUser方法;可以看到进入了继承自AbstractUserDetailsAuthenticationProvider的DaoAuthenticationProvider,在这个类中实现了retrieveUser方法;
7、可以看到在这个方法中通过UserDetailsService的loadUserByUsername方法获取了用户信息。后续可以自己实现此方法。
8、跟进此方法,可以看到获取成功之后会封装成User对象;如果没有获取到会抛出异常
9、继续回到AbstractUserDetailsAuthenticationProvider中执行preAuthenticationChecks.check方法;会在此方法中进行账号是否锁定、是否可用、是否过期等验证;
10、接下来会在additionalAuthenticationChecks方法中进行密码匹配;如果匹配失败会抛出异常
11、然后在createSuccessAuthentication方法中进行Authentication的重新封装;
12、回到ProviderManager通过eventPublisher.publishAuthenticationSuccess进行认证成功事件的发布。
13、然后回到AbstractAuthenticationProcessingFilter,通过successfulAuthentication方法设置SecurityContext