1.認證流程流程
經過斷點調試,能夠看到在UsernamepasswordAuthenticationFilter中構造了一個
UsernamePasswordAuthenticationToken對象spring
打開UsernamePasswordAuthenticationToken可得知,該實現類是Authentication的子類,由於Authentication是封裝了用戶的信息。
在該構造函數中,其中super(null)是調用了父類的方法
,
父類的方法以下:app
public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) { if (authorities == null) { //爲空時,須要賦個默認的權限,由於此時還未進行身份認證 this.authorities = AuthorityUtils.NO_AUTHORITIES; return; } for (GrantedAuthority a : authorities) { if (a == null) { throw new IllegalArgumentException( "Authorities collection cannot contain any null elements"); } } ArrayList<GrantedAuthority> temp = new ArrayList<GrantedAuthority>( authorities.size()); temp.addAll(authorities); this.authorities = Collections.unmodifiableList(temp); }
setAuthenticated(false) 表明當前存進去的principal/credentials是否通過身份認證,此時確定是沒有的。ide
setDetails(request, authRequest);
該方法會把請求的一些信息設置到UsernamePasswordAuthenticationToken裏面去,包括當前發起請求的IP,Session等函數
return this.getAuthenticationManager().authenticate(authRequest); //往AuthenticationManager靠攏
AuthenticationManager該類自己不包含校驗的邏輯,它的做用是用來管理AuthenticationProvider。
該方法會請求進入:ProviderManager.authenticate()方法,該類實現了AuthenticationManager接口post
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { // if (!provider.supports(toTest)) { continue; } /* getProviders() 拿到全部的AuthenticationProvider,校驗邏輯都是在Provider中 由於不一樣的登陸方式,它的認證邏輯是不通 的,目前使用的用戶名+密碼的方式,後續還會有第三方登陸,手機 號驗證碼登陸等,provider.supports(toTest)是否支持當前的登陸方式(判斷) 對於用戶名密碼方式,它傳遞的token是:UsernamePasswordAuthenticationToken,而對於第三方登陸時的驗證方式則是SocialAuthenticationToken */ if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { //具體執行校驗邏輯 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); 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 = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { } catch (AuthenticationException e) { lastException = e; } } if (result != null) { if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { ((CredentialsContainer) result).eraseCredentials(); } eventPublisher.publishAuthenticationSuccess(result); return result; } if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } prepareException(lastException, authentication); throw lastException; }
provider.authenticate()實現類是寫在AuthenticationProvider的實現類AbstractUserDetailsAuthenticationProvider中this
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, 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 { //獲取用戶信息,具體實現類在:DaoAuthenticationProvider.retrieveUser() user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { logger.debug("User '" + username + "' not found"); if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } try { preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } postAuthenticationChecks.check(user); if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); }
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { UserDetails loadedUser; try { loadedUser = this.getUserDetailsService().loadUserByUsername(username); /** getUserDetailService在調用咱們提供的UserDetailService的實現,也就是:MyUserDetailsService */ } catch (UsernameNotFoundException notFound) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); passwordEncoder.isPasswordValid(userNotFoundEncodedPassword, presentedPassword, null); } throw notFound; } catch (Exception repositoryProblem) { throw new InternalAuthenticationServiceException( repositoryProblem.getMessage(), repositoryProblem); } if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; }
拿到用戶信息以後,回到AbstractUserDetailsAuthenticationProviderdebug
try { preAuthenticationChecks.check(user); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); //在dao裏校驗密碼是否匹配 }
它會預檢查用戶是否過時,是否禁用,以後檢查密碼是否匹配。
預檢查以後還會有後置檢查3d
postAuthenticationChecks.check(user); //
全部檢查都經過,就會認爲用戶的認證是成功的。調試
return createSuccessAuthentication(principalToReturn, authentication, user); protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken( principal, authentication.getCredentials(), authoritiesMapper.mapAuthorities(user.getAuthorities())); result.setDetails(authentication.getDetails()); return result; }
再次new 了UsernamePasswordAuthenticationToken對象,區別再與構造方法不一樣,傳遞的參數不一樣,這個時候權限,用戶信息都已經拿到code
2.認證結果如何在多個請求之間共享
3.獲取用戶認證的信息