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