Pig4Cloud之登陆验证(一)客户端认证处理
创始人
2024-03-25 11:07:13
0

前端登陆

handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.$store.dispatch("LoginByUsername", this.loginForm).then(() => {this.$router.push({path: this.tagWel.value});}).catch(() => {this.refreshCode();});}});}

看一下LoginByUsername,在/src/store/modules/user.js中

const scope = 'server'export const loginByUsername = (username, password, code, randomStr) => {const grant_type = 'password'let dataObj = qs.stringify({'username': username, 'password': password})let basicAuth = 'Basic ' + window.btoa(website.formLoginClient)// 保存当前选中的 basic 认证信息setStore({name: 'basicAuth',content: basicAuth,type: 'session'})return request({url: '/auth/oauth2/token',headers: {isToken: false,Authorization: basicAuth},method: 'post',params: {randomStr, code, grant_type, scope},data: dataObj})
}

客户端认证

当访问 OAuth2 相关接口时(/oauth2/token/oauth2/introspect/oauth2/revoke),授权服务器需要进行客户端认证。
Spring Authorization Server 截至目前支持如下五种客户端认证方式:client_secret_basicclient_secret_postclient_secret_jwtprivate_key_jwtnone (针对公共客户端)

OAuth2ClientAuthenticationFilter

实现客户端认证的拦截器就是 OAuth2ClientAuthenticationFilter。 其核心代码如下:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {if (!this.requestMatcher.matches(request)) {filterChain.doFilter(request, response);return;}try {Authentication authenticationRequest = this.authenticationConverter.convert(request);if (authenticationRequest instanceof AbstractAuthenticationToken) {((AbstractAuthenticationToken) authenticationRequest).setDetails(this.authenticationDetailsSource.buildDetails(request));}if (authenticationRequest != null) {Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);}filterChain.doFilter(request, response);} catch (OAuth2AuthenticationException ex) {this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);}
}

其核心逻辑就是通过 authenticationConverter 从 request 中解析出客户端认证信息,构建成 Authentication,再通过 authenticationManager 对 Authentication 进行认证。

DelegatingAuthenticationConverter

authenticationConverter 的类型实际上是 DelegatingAuthenticationConverter,它持有一个 AuthenticationConverter 列表(不同的认证请求,其参数不同,所以会有不同的AuthenticationConverter实现类)。
DelegatingAuthenticationConverter 在解析请求时会遍历 AuthenticationConverter 列表,当某个 AuthenticationConverter 解析成功时,立即返回,这也能确定此请求是什么认证方式,后续再执行对应的认证逻辑。

private AuthenticationConverter authenticationConverter;public OAuth2ClientAuthenticationFilter(AuthenticationManager authenticationManager,RequestMatcher requestMatcher) {Assert.notNull(authenticationManager, "authenticationManager cannot be null");Assert.notNull(requestMatcher, "requestMatcher cannot be null");this.authenticationManager = authenticationManager;this.requestMatcher = requestMatcher;this.authenticationConverter = new DelegatingAuthenticationConverter(Arrays.asList(new JwtClientAssertionAuthenticationConverter(),new ClientSecretBasicAuthenticationConverter(),new ClientSecretPostAuthenticationConverter(),new PublicClientAuthenticationConverter()));}

ProviderManager

authenticationManager 的类型实际上是 ProviderManager,它持有一个 AuthenticationProvider 列表(不同的认证方式,其认证逻辑不同,所以会有不同的AuthenticationProvider实现类)。

AuthenticationManager 接口的默认实现为 ProviderManager
image

authenticationManager调用authenticate
image

调用 AuthenticationProvider 中的 supports(Class authentication) 方法,判断是否支持当前的 Authentication 请求。

public Authentication authenticate(Authentication authentication)throws AuthenticationException {......for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}

只有支持当前 Authentication 请求的 AuthenticationProvider 才会继续后续逻辑处理。
然后调用 AuthenticationProvider 中的 authenticate 方法进行身份认证。
此处的provider为ClientSecretAuthenticationProvider
image

public Authentication authenticate(Authentication authentication)throws AuthenticationException {......for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}......try {result = provider.authenticate(authentication);......}

ClientSecretAuthenticationProvider中调用RegisteredClientRepository,通过clientId去数据库查询client
image
image

如果认证成功且返回的结果不为 null,则执行 authentication details 的拷贝逻辑。

try {result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}
}......private void copyDetails(Authentication source, Authentication dest) {if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;token.setDetails(source.getDetails());}
}

如果发生 AccountStatusExceptionInternalAuthenticationServiceException 异常,则会通过Spring事件发布器AuthenticationEventPublisher 发布异常事件。

catch (AccountStatusException e) {prepareException(e, authentication);// SEC-546: Avoid polling additional providers if auth failure is due to// invalid account statusthrow e;
}
catch (InternalAuthenticationServiceException e) {prepareException(e, authentication);throw e;
}......private void prepareException(AuthenticationException ex, Authentication auth) {eventPublisher.publishAuthenticationFailure(ex, auth);
}

如果异常为其它类型的 AuthenticationException,则将此异常设置为lastException并返回。

catch (AuthenticationException e) {lastException = e;
}

如果认证结果为 null,且存在父 AuthenticationManager,则调用父 AuthenticationManager 进行同样的身份认证操作,其处理逻辑基本同上。

if (result == null && parent != null) {// Allow the parent to try.try {result = parentResult = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {// ignore as we will throw below if no other exception occurred prior to// calling parent and the parent// may throw ProviderNotFound even though a provider in the child already// handled the request}catch (AuthenticationException e) {lastException = parentException = e;}
}

如果认证结果不为 null,同时,此时的 eraseCredentialsAfterAuthentication 参数为 true,且此时认证后的Authentication 实现了 CredentialsContainer 接口,那么即调用 CredentialsContainer 接口的凭据擦除方法,即eraseCredentials,擦除相关凭据信息。

if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {// Authentication is complete. Remove credentials and other secret data// from authentication((CredentialsContainer) result).eraseCredentials();}// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published itif (parentResult == null) {eventPublisher.publishAuthenticationSuccess(result);}return result;
}

其中,有一个防止重复发布 AuthenticationSuccessEvent 事件的处理,即 parentResult 为空。如果 parentResult为 null,则代表父 AuthenticationManager 不存在或者没有身份认证成功,也即没有发布过 AuthenticationSuccessEvent 事件。此时,便由此处发布 AuthenticationSuccessEvent 事件。

如果lastException 为 null,则代表当前的 Authentication 并没有对应支持的 Provider。此时,便会抛出相应异常。

if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));
}

如同防止重复发布 AuthenticationSuccessEvent 事件的处理一样,也有一个防止 AbstractAuthenticationFailureEvent 事件重复发布的逻辑处理。如果 parentException 为 null,则代表父AuthenticationManager 不存在、没有进行身份认证或者发布过 AbstractAuthenticationFailureEvent 事件,此时,便由此处发布 AbstractAuthenticationFailureEvent 事件。

if (parentException == null) {prepareException(lastException, authentication);
}throw lastException;

最后,抛出 lastException。

博客园
腾讯云
掘金
简书

相关内容

热门资讯

每周股票复盘:广电网络(600... 截至2025年12月26日收盘,广电网络(600831)报收于4.2元,较上周的4.36元下跌3.6...
每周股票复盘:新疆火炬(603... 截至2025年12月26日收盘,新疆火炬(603080)报收于22.85元,较上周的22.73元上涨...
每周股票复盘:瀚川智能(688... 截至2025年12月26日收盘,瀚川智能(688022)报收于15.3元,较上周的14.42元上涨6...
每周股票复盘:中粮糖业(600... 截至2025年12月26日收盘,中粮糖业(600737)报收于17.27元,较上周的17.18元上涨...
每周股票复盘:马钢股份(600... 截至2025年12月26日收盘,马钢股份(600808)报收于4.22元,较上周的3.82元上涨10...
每周股票复盘:内蒙一机(600... 截至2025年12月26日收盘,内蒙一机(600967)报收于16.34元,较上周的16.05元上涨...
富达基金投顾业务负责人戴旻:封... 由三亚市人民政府主办,《财经》杂志、财经网、《财经智库》、三亚中央商务区管理局、三亚经济研究院承办的...
海南大谷国际园区董事长张焱:以... 由三亚市人民政府主办,《财经》杂志、财经网、《财经智库》、三亚中央商务区管理局、三亚经济研究院承办的...
每周股票复盘:汉马科技(600... 截至2025年12月26日收盘,汉马科技(600375)报收于5.87元,较上周的5.92元下跌0....
每周股票复盘:中天科技(600... 截至2025年12月26日收盘,中天科技(600522)报收于18.4元,较上周的18.37元上涨0...