1.首先通過的是一個UsernamePasswordAuthenticationFilter過濾器,該過濾器在獲取到用戶名和密碼以後就去構建一個 UsernamePasswordAuthenticationToken的對象,該對象是Authentication(即用戶的認證信息) UsernamePasswordAuthenticationFilter.javajava
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
UsernamePasswordAuthenticationToken.java緩存
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { // 這個地方調用父類的該AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities),可是該方法須要傳遞一組權限進來,在驗證玩以前仍是沒有任何權限信息的,因此傳遞一個空進來。 super((Collection)null); //對應用戶名 this.principal = principal; //對應密碼 this.credentials = credentials; //表示信息沒通過任何認證,因此是false this.setAuthenticated(false); }
UsernamePasswordAuthenticationFilter.javasession
//將請求的信息放入上面生成的UsernamePasswordAuthenticationToken裏面 this.setDetails(request, authRequest);
直接調用了它的子類(ProviderManager)的authenticate方法return this.getAuthenticationManager().authenticate(authRequest);app
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); Iterator var6 = this.getProviders().iterator(); //遍歷是否支持當前的校驗方式 while(var6.hasNext()) { AuthenticationProvider provider = (AuthenticationProvider)var6.next(); //對校驗方式進行判斷。 if (provider.supports(toTest)) { if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { result = provider.authenticate(authentication); if (result != null) { this.copyDetails(authentication, result); break; } } catch (AccountStatusException var11) { this.prepareException(var11, authentication); throw var11; } catch (InternalAuthenticationServiceException var12) { this.prepareException(var12, authentication); throw var12; } catch (AuthenticationException var13) { lastException = var13; } } } if (result == null && this.parent != null) { try { result = this.parent.authenticate(authentication); } catch (ProviderNotFoundException var9) { ; } catch (AuthenticationException var10) { lastException = var10; } } if (result != null) { if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) { ((CredentialsContainer)result).eraseCredentials(); } this.eventPublisher.publishAuthenticationSuccess(result); return result; } else { if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}")); } this.prepareException((AuthenticationException)lastException, authentication); throw lastException; } }
這段代碼遍歷循環判斷是否支持該認證方式。ide
result = provider.authenticate(authentication);函數
在AbstractUserDetailsAuthenticationProvider.java裏面post
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { //獲取到用戶的信息(UserDetails) user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); } catch (UsernameNotFoundException var6) { this.logger.debug("User '" + username + "' not found"); if (this.hideUserNotFoundExceptions) { throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } throw var6; } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { this.preAuthenticationChecks.check(user); this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); } catch (AuthenticationException var7) { if (!cacheWasUsed) { throw var7; } cacheWasUsed = false; user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); this.preAuthenticationChecks.check(user); this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); } this.postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (this.forcePrincipalAsString) { principalToReturn = user.getUsername(); } return this.createSuccessAuthentication(principalToReturn, authentication, user); }
result = provider.authenticate(authentication)調用的其實是調用DaoAuthenticationProvider的authenticate(authentication),該方法裏面調用了 loadedUser = this.getUserDetailsService().loadUserByUsername(username);其實是調用咱們本身寫的UserDetailService的實現類。這個時候就獲取到了UserDetail對象了: MyDetailService.javaui
@Component//TODO 使之成爲用戶的bean public class MyDetailService implements UserDetailsService { @Autowired PasswordEncoder passwordEncoder; Logger logger = LoggerFactory.getLogger(MyDetailService.class); [@Override](https://my.oschina.net/u/1162528) public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //TODO 根據用戶名查找用戶信息 logger.info("登錄用戶名:"+s); String encode = passwordEncoder.encode("123456"); logger.info("登錄用戶名:"+encode); return new User(s,encode,true,true,true,true, AuthorityUtils.createAuthorityList("admin")); } }
AbstractUserDetailsAuthenticationProvider.java裏面進行前置,附加,後置檢查。this
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); } catch (UsernameNotFoundException var6) { this.logger.debug("User '" + username + "' not found"); if (this.hideUserNotFoundExceptions) { throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } throw var6; } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { //預檢查 this.preAuthenticationChecks.check(user); //附加檢查 this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); } catch (AuthenticationException var7) { if (!cacheWasUsed) { throw var7; } cacheWasUsed = false; user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); this.preAuthenticationChecks.check(user); this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); } //後置檢查 this.postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (this.forcePrincipalAsString) { principalToReturn = user.getUsername(); } //成功建立 return this.createSuccessAuthentication(principalToReturn, authentication, user); }
this.createSuccessAuthentication(principalToReturn, authentication, user)方法以下:.net
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities())); result.setDetails(authentication.getDetails()); return result; }
該方法重新建立了一個UsernamePasswordAuthenticationToken對象,該對象已經帶有認證的信息。
此時調用的構造函數以下
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { //此時具有了權限了信息 super(authorities); this.principal = principal; this.credentials = credentials; //此時已經校驗經過了。 super.setAuthenticated(true); }
到這裏已經完成認證獲取到了咱們須要的Authentication實例對象,須要沿着圖返回去。
在AbstractAuthenticationProcessingFilter.java的public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)裏面調用了successfulAuthentication(request, response, chain, authResult);這個方法就是調用咱們本身寫的成功處理器。若是期間出錯就會調用咱們本身建立的失敗處理器。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (!requiresAuthentication(request, response)) { chain.doFilter(request, response); return; } if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } Authentication authResult; try { authResult = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed // authentication return; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); }
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } //調用咱們本身的處理器 successHandler.onAuthenticationSuccess(request, response, authResult); }
認證請求緩存的過程
認證流程中SecurityPersistenceFilter的位置
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } //將認證放入了SecurityContext類中而後將SecurityContext放入SecurityContextHolder裏面。 SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } successHandler.onAuthenticationSuccess(request, response, authResult); }
SecurityContext類其實是Authentication的包裝。
SecurityContextHolder其實是ThreadLocal的實現,使得認證的信息在線程的其餘方法均可以獲取。
SecurityContextPersistenceFilter進來的時候檢查session是否有認證信息,有就放入線程,後面的類,方法均可以使用,沒有就直接進入後的過濾器進行校驗。響應的時候就檢查線程是否有有驗證信息,有就放入session
@GetMapping("getAuthentication") public Authentication getAuthentication(){ return SecurityContextHolder.getContext().getAuthentication(); }
@GetMapping("getAuthentication2") public Object getAuthentication2(@AuthenticationPrincipal UserDetails userDetails){ return userDetails; }