好多年没有接触oauth2.0,最近接手一个老项目,项目扩展了手机号密码登录、手机号验证码登录多重认证,遇到一些问题,大概梳理一下过程
开发环境
spring-security-oauth2 2.3.4.RELEASE版本
spring-security-web 5.3.9.RELEASE版本
获取token接口
获取token
POST /oauth/token?grant_type=phone_sms
application/form-data
Authorization: Basic d3g6d3g=
参数:
phone=xxx
smsCode=xxx
刷新token
POST /oauth/token?grant_type=refresh_token&refresh_token=134afb26-6491-479c-9edb-cb603f86dcd2
Authorization: Basic d3g6d3g=
认证过程
Basic认证
org.springframework.security.web.authentication.www.BasicAuthenticationFilter#doFilterInternal拦截Authorization请求头

匿名认证/token认证
authenticationConverter.convert根据request请求头header获取Authorition: Basic请求头
这一步如果获取的authRequest=null(不存在Authorization头;Authorization头不是Basic认证),则进入filterChain进行校验,所以匿名认证、bearer认证都会走这一步
核心类在org.springframework.security.web.access.intercept.FilterSecurityInterceptor#doFilter, 处理逻辑在父类中处理

1). authenticateIfRequired方法尝试用户认证,进入authenticationManager.authenticate(authentication)方法获取认证,真正的认证是在org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager#authenticate中进行

2). 回到AbstractSecurityInterceptor类中的accessDecisionManager.decide(authenticated, object, attributes)方法,在方法org.springframework.security.access.vote.AffirmativeBased#decide中如果校验失败,直接抛出401异常

Basic认证
1). 调用Authentication authResult = this.authenticationManager.authenticate(authRequest);进行client认证,在实现类中org.springframework.security.authentication.ProviderManager#authenticate获取client登录信息

可以看到这里有两个provider,使用DaoAuthenticationProvider实现类retrieveUser方法获取client信息 。这一步也有一个**[扩展点2]**

最终在ClientDetailsUserDetailsService#loadUserByUsername中根据Basic中信息校验并获取client信息。这一步也有一个**[扩展点3]**

至此也就获取到了详细的clientDetails信息,也就是在AuthorizationServerConfigurerAdapter#configure(org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer)实现方法中通过client.inMemory()中定义的client信息,这里既可以是InMemory存储,也可以是Jdbc存储。
2). 完成client认证后进入生成token阶段
进入org.springframework.security.oauth2.provider.endpoint.TokenEndpoint#postAccessToken,进行一系列的校验、组装参数,通过granter去获取token ** [扩展点4]**

这一步非常关键,可以看到Implicit、ClientCredentials认证模式,还有PhoneSmsCustom自定义模式,RefreshToken也在这里处理

又看到CompositeTokenGranter类,这里使用到策略模型根据不同的grant_type使用不同的granter校验用户获取用户信息,可以看到AbstractTokenGranter的实现 [扩展点5]

最后一步org.springframework.security.oauth2.provider.token.AbstractTokenGranter#getAccessToken获取token进行持久化存储

如果采用redis缓存token的话,也可以在redis中看到为什么会存储很多的token

扩展点
- 扩展点1:
- 扩展点2:provider扩展,重写DaoAuthenticationProvider类,例如:DecodePwdAuthenticationProvider
- 扩展点3:UserServiceDetails扩展,默认情况下只有一个默认方法
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;,这一步可以扩展不同的方法给granter使用 - 扩展点4:granter扩展,继承AbstractTokenGranter类,实现策略模型,根据不同的granter实现不同的用户校验逻辑。例如: PhoneSmsCustomTokenGranter
- 扩展点5:重写AbstractTokenGranter类的getOAuth2Authentication,调用UserServiceDetails的实现方法获取用户数据,生成OAuth2Authentication用户认证。例如:AbstractCustomTokenGranter
1078

被折叠的 条评论
为什么被折叠?



