SpringSecurity 密碼驗證流程

一、通過debug源碼,解析,更加清楚的瞭解核心的原理,出現問題可以更好的解決

二、密碼驗證流程

1 默認使用 UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter

  • 執行方法:
    
    	public Authentication attemptAuthentication(HttpServletRequest request,
    			HttpServletResponse response) throws AuthenticationException {
    		if (postOnly && !request.getMethod().equals("POST")) {
    			throw new AuthenticationServiceException(
    					"Authentication method not supported: " + request.getMethod());
    		}
    		String username = obtainUsername(request);
    		String password = obtainPassword(request);
    		if (username == null) {
    			username = "";
    		}
    		if (password == null) {
    			password = "";
    		}
    		username = username.trim();
    		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
    				username, password);
    		// Allow subclasses to set the "details" property
    		setDetails(request, authRequest); (這裏最終是AbstractAuthenticationToken)
    		return this.getAuthenticationManager().authenticate(authRequest);
    	} 
    

2 return this.getAuthenticationManager().authenticate(authRequest); 執行這個方法後,跳入到 AuthenticationManager 這個接口,以其一個實現類來實現這個方法。

 ```
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
 ```

3 ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean

 ```
     public Authentication authenticate(Authentication authentication)
                throws AuthenticationException {
            Class<? extends Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            Authentication result = null;
            boolean debug = logger.isDebugEnabled(); 
            --AuthenticationProvider 校驗的是這個對象
            for (AuthenticationProvider provider : getProviders()) {
                if (!provider.supports(toTest)) {
                    continue;
                }
                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);
                    // 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 = 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 = e;
                }
            }
            if (result != null) {
                if (eraseCredentialsAfterAuthentication
                        && (result instanceof CredentialsContainer)) {
                    // Authentication is complete. Remove credentials and other secret data
                    // from authentication
                    ((CredentialsContainer) result).eraseCredentials();
                }
                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}"));
            }
            prepareException(lastException, authentication);
            throw lastException;
     }
 ```

4 進入 public interface AuthenticationProvider

  ```
  	Authentication authenticate(Authentication authentication) throws AuthenticationException;
  ```

5 進入 abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware,實現了AuthenticationProvider 接口的authenticate(authentication)方法

 ```
 public Authentication authenticate(Authentication authentication)
 			throws AuthenticationException {
 		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
 				messages.getMessage(
 						"AbstractUserDetailsAuthenticationProvider.onlySupports",
 						"Only UsernamePasswordAuthenticationToken is supported"));
 		// Determine username
 		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 = 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) {
 				// There was a problem, so try again after checking
 				// we're using latest data (i.e. not from the cache)
 				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 abstract void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)
                  throws AuthenticationException;
    

6 通過實現該抽象類的子類 class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider,重寫 additionalAuthenticationChecks方法

 ```
 protected void additionalAuthenticationChecks(UserDetails userDetails,
 			UsernamePasswordAuthenticationToken authentication)
 			throws AuthenticationException {
 		Object salt = null;
 		if (this.saltSource != null) {
 			salt = this.saltSource.getSalt(userDetails);
 		}
 		if (authentication.getCredentials() == null) {
 			logger.debug("Authentication failed: no credentials provided");
 			throw new BadCredentialsException(messages.getMessage(
 					"AbstractUserDetailsAuthenticationProvider.badCredentials",
 					"Bad credentials"));
 		}
 		String presentedPassword = authentication.getCredentials().toString();
 		//驗證密碼, passwordEncoder 是客戶端穿回來的密碼,userDetails.getPassword()是數據庫中的密碼
 		if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),presentedPassword, salt)) {
 			logger.debug("Authentication failed: password does not match stored value");
 			throw new BadCredentialsException(messages.getMessage(
 					"AbstractUserDetailsAuthenticationProvider.badCredentials",
 					"Bad credentials"));
 		}
 	}
 ```

三、其他有關SpringSecurity的筆記,包括核心認識,demo地址

SpringSecurity初步學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章