OAuth2.0登录认证
OAuth2.0生成token是一套规范的流程,并且支持个性化,流程大概分为这么几步
核心流程都在OAuth2TokenEndpointFilter中,当然在正式接收请求前,一个完整的认证流程一般需要经过这么几步:
1.接收前处理
1.1 针对请求进行校验,判断是否auth/token,拼接key进行验证码验证
extends OncePerRequestFilter并重写doFilterInternal方法,添加至http.addFilterBefore()完成
1.2 为请求的登录密码进行解密
extends OncePerRequestFilter并重写doFilterInternal方法,添加至http.addFilterBefore()完成
1.2 获取客户端支持的认证方式
implements RegisteredClientRepository完成
2.正式接收请求
2.1 验证请求的授权方式(OAuth2TokenEndpointFilter)
String[] grantTypes = request.getParameterValues(OAuth2ParameterNames.GRANT_TYPE);if (grantTypes == null || grantTypes.length != 1) {throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE);}
2.2 根据请求的授权方式,遍历所有的转换器(实现自己的个性化转换器),得到自己的转换器(通过重写的support方法判断)
public Authentication convert(HttpServletRequest request) {Assert.notNull(request, "request cannot be null");for (AuthenticationConverter converter : this.converters) {Authentication authentication = converter.convert(request);if (authentication != null) {return authentication;}}return null;}
个性化转换器:implements AuthenticationConverter完成
2.3 拿到转换器后,接下来一般会去校验自己的个性化参数,如
@Overridepublic void checkParams(HttpServletRequest request) {MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);// testParam (REQUIRED)String testParam = parameters.getFirst(SecurityConstants.TEST_PARAM);if (!StringUtils.hasText(testParam) || parameters.get(SecurityConstants.TEST_PARAM).size() != 1) {OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, SecurityConstants.TEST_PARAM,OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);}}
2.4 通过参数校验后,就可以创建自己的个性化登录认证对象了,如
@Overridepublic OAuth2TestAuthenticationToken buildToken(Authentication clientPrincipal, Set<String> requestedScopes, Map<String, Object> additionalParameters) {return new OAuth2TestAuthenticationToken(new AuthorizationGrantType(SecurityConstants.TEST),clientPrincipal, requestedScopes, additionalParameters);}
个性化认证对象:extends AbstractAuthenticationToken完成
2.5 有了这个对象,认证管理器接下来会去遍历认证提供值,得到你的个性化认证提供者
for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}
。。。。
}
个性化认证提供者:implements AuthenticationProvider
2.6 拿到认证提供者后,去验证该授权方式是否支持,如
@Overridepublic void checkClient(RegisteredClient registeredClient) {assert registeredClient != null;if (!registeredClient.getAuthorizationGrantTypes().contains(new AuthorizationGrantType(SecurityConstants.TEST))) {throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);}}
2.7 验证通过后,会将你的请求参数构建成认证对象,进行认证,如
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = buildToken(reqParameters);LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);Authentication usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
2.8 通过后,生成accessToken、refreshToken,在认证成功处理器传给前端
总结
登录认证
验证码过滤 Filter
密码过滤 Filter
客户端认证方式 RegisteredClientRepository(ClientSecretAuthenticationProvider内部调用)
验证认证类型规范
请求转换器转换认证对象
认证提供方认证
验证客户端认证类型是否支持
调用相关认证实现类(AbstractUserDetailsAuthenticationProvider)AuthenticationProvider
调用 UserDetailsService
生成token信息
核心就是多个Provider 与 多个Converter 之间的调用来处理各种信息,完成各种操作
如
ClientSecretAuthenticationProvider 得到客户认证类型信息,调用RegisteredClientRepository
AbstractUserDetailsAuthenticationProvider 完成认证逻辑,调用你的service,impl完成认证
OAuth2RefreshTokenAuthenticationProvider 完成token刷新
等等,不同的工作通过不同的Provider完成