springSecurity之AuthenticationProvider用法解析
作者:chihaihai
AuthenticationProvider解析
首先进入到AuthenticationProvider源码中可以看到它只是个简单的接口里面也只有两个方法:
public interface AuthenticationProvider { // 具体认证流程 Authentication authenticate(Authentication authentication) throws AuthenticationException; // supports函数用来指明该Provider是否适用于该类型的认证,如果不合适,则寻找另一个Provider进行验证处理。 boolean supports(Class<?> authentication); }
AuthenticationProvider是用户自定义身份认证,认证流程顶级接口。
唯一作用即使用来进行身份验证,同时springSecurity也为我们提供了很多方便的实现类。
当我们没有指定相关AuthenticationProvider 对象时springSecurity默认使用的就时上图中的DaoAuthenticationProvider进行验证。
也就是最常见的账户名密码的方式。但是实际开发中我们往往需要实现自定义认证流程比如最常见的短信验证码,第三方登录等等。
这个时候我们就可以通过实现自己的 AuthenticationProvider方式来进行自定义认证。
只需要在WebSecurityConfigurerAdapter适配器类的config方法中加入自己实现的AuthenticationProvider 即可。
@Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authenticationProvider()); }
说到AuthenticationProvider就离不开AuthenticationManager这个接口
public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; }
同样的AuthenticationManager也是一个顶级接口,可以看到它其中也定义了一个跟AuthenticationProvider一摸一样的方法。
如果说Auth ntic ationProvider是对认证的具体实现,则AuthenticationManager则是对我们众多AuthenticationProvider的一个统一管理。
Authentication Ma nager的实现有很多,通常使用ProviderManager对认证请求链进行管理。
从源码中可以看到
ProviderManager提供了一个list对AuthenticationProvider进行统一管理,即一个认证处理器链来支持同一个应用中的多个不同身份认证机制,ProviderManager将会根据顺序来进行验证。
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { // 如果支持认证实现类就继续处理 if (!provider.supports(toTest)) { continue; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { // 调用实现类的authenticate方法进行真实业务逻辑认证处理 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } 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; } } 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 it if (parentResult == null) { // //发送认证成功事件 eventPublisher.publishAuthenticationSuccess(result); } return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } // If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent // This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。