pigx登录代码走查


pigx登录代码走查

大体流程

6. /oauth/token 接收

  1. 因为发送的是 POST /oauth/token,所以会进入 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint#postAccessToken

  2. 在这个方法里面有个参数是principal,根据调试可以发现,它的实际类型是UsernamePasswordAuthenticationToken。在这个方法里面还有个参数是parameters,它是一个map,里面存放了passwordgrant_typeusername这三个字段

  3. 方法一开始判断principal是否是org.springframework.security.core.Authentication的子类,不是的话抛出异常,告知用户增加一个合适的认证过滤器

  4. principal里面实际存储的是clientIdclientSecret,所以方法从principal里面取出clientId,然后通过ClientDetailsService将数据库里面的Client详细信息获取出来

  5. 用parameters里面的passwordgrant_typeusername,以及刚才获取到的Client详细信息(clientIdscopes),构造出一个tokenRequest

  6. 然后方法中写了一个很奇怪的逻辑,将clientIdtokenRequest.getClientId进行比较。这个比较有意义吗?难道还会不同?理解不了老外的想法

  7. 对第4步获取到的Client详细信息,将它与第5步获取到的tokenRequest进行比较,比较scope是否合法。太奇怪了,tokenRequest不是从Client详细信息那里构造出来的吗?为啥还要判断scope是否合法?老外的想法太奇怪了

  8. 判断grant_type是否是implicit,是的话抛出异常,告知用户/oauth/token不支持implicit方式登录

  9. 判断grant_type是否是authorization_code(授权码模式),并且code是否不为空,是的话说明是授权码模式登录,我们用的是password(密码模式),所以这段逻辑先不看

  10. 判断grant_type是否是refresh_token(刷新token模式),并且refresh_token是否不为空,是的话说明是刷新token模式,我们用的是password(密码模式),所以这段逻辑先不看

  11. 取出tokenGranter(我不知道这是啥东西),然后调用它的grant方法,方法的参数是授权类型(也就是password)和第5步生成的tokenRequest

  12. grant方法里面,是调用代理对象delegate(我不太清楚这个是什么东西)的grant方法

  13. 代理对象是CompositeTokenGranter类,它里面有个tokenGranters属性,该属性存储了五个TokenGranter

    其中ResourceOwnerPasswordTokenGranter是我们所需要的TokenGranter

  14. org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant方法里面,首先会用clientDetailsService.loadClientByClientId取出ClientDetails,验证这个clientId是否有password(密码模式)认证类型,没有的话抛出异常

  15. 使用ClientDetailstokenRequest,调用getAccessToken方法

  16. 调用getOAuth2Authentication方法,进入到org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter#getOAuth2Authentication

  17. tokenRequest中取出usernamepassword,新建出UsernamePasswordAuthenticationToken,再将tokenRequest里面的parameters Map表设置到该token的details属性里面

  18. 进入org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.AuthenticationManagerDelegator#authenticate。然后再进入到org.springframework.security.authentication.ProviderManager#authenticate

  19. ProviderManager#authenticate方法里面,有两个AuthenticationProvider,它们分别是AnonymousAuthenticationProviderMobileAuthenticationProvider,恰好两个Provider的supports方法都表明UsernamePasswordAuthenticationToken不是它们继承的类,所以这些Provider的authenticate方法是不会进入的

  20. 如果上面两个都没有做过认证处理,那么再找父类,让父类来认证。父类也有providers,它里面只有一个DaoAuthenticationProvider,这个AuthenticationProvider支持UsernamePasswordAuthenticationToken,所以调用DaoAuthenticationProvider的authenticate方法

  21. UsernamePasswordAuthenticationToken取出username。通过usernameuserCache取出UserDetails,如果是null,则调用retrieveUser方法获取用户信息,在这个方法里面调用了loadUserByUsername

  22. 对于返回的用户详情,首先进行预检查,依次检查是否被锁定、是否被禁用、是否账号过期。

  23. 然后再进入additionalAuthenticationChecks,在这个方法里面,取出UsernamePasswordAuthenticationToken里面的密码原文,对比UserDetails里面的密码密文,使用passwordEncoder.matches进行密码校验

  24. 最后进入org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.DefaultPostAuthenticationChecks#check,检查密码是否过期

  25. 经过前面三步,都没有报错的话,说明用户输入的账号密码是正确的。将用户详情存入缓存。调用createSuccessAuthentication

  26. createSuccessAuthentication方法里面,首先要判断密码是否要更新。我们在登录呢,不需要更新密码。然后进入父类的createSuccessAuthentication

  27. 在父类的createSuccessAuthentication里面,使用账号、密码、权限构造出一个token,再将认证里面的详情信息(也就是那个Map的信息)写入token的details里面,返回UsernamePasswordAuthenticationToken

  28. 回到21步,擦除密码,将密码广播出去。返回上级AuthenticationProvider,也擦除一遍密码,返回认证信息

  29. 很多函数结束返回了,最终又返回到org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter#getOAuth2Authentication,调用getRequestFactory().createOAuth2Request(client, tokenRequest),移除掉map中的password,构造出modifiable,使用new OAuth2Request(modifiable, client.getClientId(), client.getAuthorities(), true, this.getScope(), client.getResourceIds(), null, null, null)生成OAuth2Request

  30. 使用刚才生成的OAuth2Request和28步返回的userAuth,构造出OAuth2Authentication

  31. 返回上层,使用tokenServices.createAccessToken创建token。进入到org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication),调用tokenStore.getAccessToken

  32. tokenStore.getAccessToken里面,先调用authenticationKeyGenerator.extractKey生成一个字符串,在它前面加上auth_to_access:再调用serializeKey,获取redis连接从里面获取bytes,将bytes反序列化就是token了…… 我不看了,现在大概意思就是,先看看redis里面有没有token,有的话返回token;没有的话,就调用 org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken) 创建token,如果有token增强器就增强token,将token存储到tokenStore里面,返回token


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM