Spring Security,没有看起来那么复杂(附源码)

权限管理是每个项目必备的功能,只是各自要求的复杂程度不同,简单的项目可能一个 Filter 或 Interceptor 就解决了,复杂一点的就可能会引入安全框架,如 Shiro, Spring Security 等。
其中 Spring Security 因其涉及的流程、类过多,看起来比较复杂难懂而被诟病。但如果能捋清其中的关键环节、关键类,Spring Security 其实也没有传说中那么复杂。本文结合脚手架框架的权限管理实现(jboost-auth 模块,源码获取见文末),对 Spring Security 的认证、授权机制进行深入分析。

使用 Spring Security 认证、鉴权机制
Spring Security 主要实现了 Authentication(认证——你是谁?)、Authorization(鉴权——你能干什么?)

认证(登录)流程
Spring Security 的认证流程及涉及的主要类如下图,

SpringSecurity认证

认证入口为 AbstractAuthenticationProcessingFilter,一般实现有 UsernamePasswordAuthenticationFilter

filter 解析请求参数,将客户端提交的用户名、密码等封装为 Authentication,Authentication 一般实现有 UsernamePasswordAuthenticationToken
filter 调用 AuthenticationManager 的 authenticate() 方法对 Authentication 进行认证,AuthenticationManager 的默认实现是
ProviderManager
ProviderManager 认证时,委托给一个 AuthenticationProvider 列表,调用列表中 AuthenticationProvider 的 authenticate()
方法来进行认证,只要有一个通过,则认证成功,否则抛出 AuthenticationException 异常(AuthenticationProvider 还有一个 supports() 方法,用来判断该 Provider
是否对当前类型的 Authentication 进行认证)
认证完成后,filter 通过 AuthenticationSuccessHandler(成功时) 或 AuthenticationFailureHandler(失败时)来对认证结果进行处理,如返回 token 或 认证错误提示
认证涉及的关键类
登录认证入口 UsernamePasswordAuthenticationFilter
项目中 RestAuthenticationFilter 继承了 UsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter 将客户端提交的参数封装为
UsernamePasswordAuthenticationToken,供 AuthenticationManager 进行认证。

RestAuthenticationFilter 覆写了 UsernamePasswordAuthenticationFilter 的 attemptAuthentication(request,response) 方法逻辑,根据
loginType 的值来将登录参数封装到认证信息 Authentication 中,(loginType 为 USER 时为 UsernameAuthenticationToken,
loginType 为 Phone 时为 PhoneAuthenticationToken),供下游 AuthenticationManager 进行认证。

认证信息 Authentication
使用 Authentication 的实现来保存认证信息,一般为 UsernamePasswordAuthenticationToken,包括

principal:身份主体,通常是用户名或手机号
credentials:身份凭证,通常是密码或手机验证码
authorities:授权信息,通常是角色 Role
isAuthenticated:认证状态,表示是否已认证
本项目中的 Authentication 实现:

UsernameAuthenticationToken: 使用用户名登录时封装的 Authentication

principal => username
credentials => password
扩展了两个属性: uuid, code,用来验证图形验证码
PhoneAuthenticationToken: 使用手机验证码登录时封装的 Authentication

principal => phone(手机号)
credentials => code(验证码)
两者都继承了 UsernamePasswordAuthenticationToken。

认证管理器 AuthenticationManager
认证管理器接口 AuthenticationManager,包含一个 authenticate(authentication) 方法。
ProviderManager 是 AuthenticationManager 的实现,管理一个 AuthenticationProvider(具体认证逻辑提供者)列表。在其 authenticate(authentication ) 方法中,对 AuthenticationProvider 列表中每一个 AuthenticationProvider,调用其 supports(Class<?> authentication) 方法来判断是否采用该
Provider 来对 Authentication 进行认证,如果适用则调用 AuthenticationProvider 的 authenticate(authentication)
来完成认证,只要其中一个完成认证,则返回。

认证提供者 AuthenticationProvider
由3可知认证的真正逻辑由 AuthenticationProvider 提供,本项目的认证逻辑提供者包括

UsernameAuthenticationProvider: 支持对 UsernameAuthenticationToken 类型的认证信息进行认证。同时使用 PasswordRetryUserDetailsChecker
来对密码错误次数超过5次的用户,在10分钟内限制其登录操作
PhoneAuthenticationProvider: 支持对 PhoneAuthenticationToken 类型的认证信息进行认证
两者都继承了 DaoAuthenticationProvider —— 通过 UserDetailsService 的 loadUserByUsername(String username) 获取保存的用户信息
UserDetails,再与客户端提交的认证信息 Authentication 进行比较(如与 UsernameAuthenticationToken 的密码进行比对),来完成认证。

用户信息获取 UserDetailsService
UserDetailsService 提供 loadUserByUsername(username) 方法,可获取已保存的用户信息(如保存在数据库中的用户账号信息)。

本项目的 UserDetailsService 实现包括

UsernameUserDetailsService:通过用户名从数据库获取账号信息
PhoneUserDetailsService:通过手机号码从数据库获取账号信息
认证结果处理
认证成功,调用 AuthenticationSuccessHandler 的 onAuthenticationSuccess(request, response, authentication) 方法,在 SecurityConfiguration 中注入 RestAuthenticationFilter 时进行了设置。 本项目中认证成功后,生成 jwt token返回客户端。

认证失败(账号校验失败或过程中抛出异常),调用 AuthenticationFailureHandler 的 onAuthenticationFailure(request, response, exception) 方法,在 SecurityConfiguration 中注入 RestAuthenticat

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值