您好,登錄后才能下訂單哦!
今天小編給大家分享一下Spring Security認證的方法是什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
本文以用戶名/密碼驗證方式為例,講解 Spring Security 的認證流程,在此之前,需要你了解 Spring Security 用戶名/密碼認證的基本配置。
Spring Security 是基于過濾器的,通過一層一層的過濾器,處理認證的流程,攔截非法請求。
處于最前面的過濾器叫做 SecurityContextPersistenceFilter
,Spring Security 是通過 Session 來存儲認證信息的,這個過濾器的 doFilter
方法在每次請求中只執行一次,作用就是,在請求時,將 Session 中的 SecurityContext 放到當前請求的線程中(如果有),在響應時,檢查縣城中是否有 SecurityContext,有的話將其放入 Session。可以理解為將 SecurityContext 進行 Session 范圍的持久化。
接著進入 UsernamePasswordAuthenticationFilter
,這是基于用戶名/密碼認證過程中的主角之一。
默認情況下,這個過濾器會匹配路徑為 /login
的 POST 請求,也就是 Spring Security 默認的用戶名和密碼登錄的請求路徑。
這里最關鍵的代碼是 attemptAuthentication
方法(由 doFilter
方法調用),源碼如下:
@Override public Authentication attemptAuthentication ( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException { if ( this.postOnly && !request.getMethod () .equals ( "POST" )) { throw new AuthenticationServiceException ( "Authentication method not supported: " + request.getMethod ()) ; } String username = obtainUsername ( request ) ; username = ( username != null ) ? username : ""; username = username.trim () ; String password = obtainPassword ( request ) ; password = ( password != null ) ? password : ""; UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken ( username, password ) ; // Allow subclasses to set the "details" property setDetails ( request, authRequest ) ; return this.getAuthenticationManager () .authenticate ( authRequest ) ; }
在 attemptAuthentication
方法代碼的第 12 行,使用從 request 中獲取到的用戶名和密碼,構建了一個 UsernamePasswordAuthenticationToken
對象,我們可以看到這個構造方法的代碼,非常簡單:
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { super((Collection)null); this.principal = principal; this.credentials = credentials; this.setAuthenticated(false); }
只是保存了用戶名和密碼的引用,并且將認證狀態設置為 false
,因為此時只是封裝了認證信息,還沒有進行認證。
我們再回到 attemptAuthentication
的代碼,在方法的最后一行,將創建好的認證信息,傳遞給了一個 AuthenticationManager
進行認證。這里實際工作的是 AuthenticationManager
的實現類 ProviderManager
。
進入 ProviderManager
可以從源碼中找到 authenticate
方法,代碼比較長,我就不貼在這里了,你可以自行查找,我簡述一下代碼中的邏輯。
ProviderManager
本身不執行認證操作,它管理著一個 AuthenticationProvider
列表,當需要對一個封裝好的認證信息進行認證操作的時候,它會將認證信息和它管理者的 Provider 們,逐一進行匹配,找到合適的 Provider 處理認證的具體工作。
可以這樣理解,ProviderManager
是一個管理者,管理著各種各樣的 Provider。當有工作要做的時候,它從來都不親自去做,而是把不同的工作,分配給不同的 Provider 去操作。
最后,它會將 Provider 的工作成果(已認證成功的信息)返回,或者拋出異常。
那么,它是怎么將一個認證信息交給合適的 Provider 的呢?
在上一部分中,我們說到,認證信息被封裝成了一個 UsernamePasswordAuthenticationToken
,它是Authentication
的子類,ProviderManager
會將這個認證信息的類型,傳遞個每個 Provider 的 supports
方法,由 Provider 來告訴 ProviderManager
它是不是支持這個類型的認證信息。
在 Spring Security 內置的 Provider 中,與 UsernamePasswordAuthenticationToken
對應的 Provider 是 DaoAuthenticationProvider
,authenticate
方法在它的父類 AbstractUserDetailsAuthenticationProvider
中。我們來看它的 authenticate
方法:
@Override public Authentication authenticate ( Authentication authentication ) throws AuthenticationException { Assert.isInstanceOf( UsernamePasswordAuthenticationToken.class, authentication, () -> this.messages.getMessage ( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported" )) ; String username = determineUsername ( authentication ) ; boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache ( username ) ; if ( user == null ) { cacheWasUsed = false; try { user = retrieveUser ( username, ( UsernamePasswordAuthenticationToken ) authentication ) ; } catch ( UsernameNotFoundException ex ) { this.logger.debug ( "Failed to find user '" + username + "'" ) ; if ( !this.hideUserNotFoundExceptions ) { throw ex; } throw new BadCredentialsException ( this.messages .getMessage ( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials" )) ; } Assert.notNull( user, "retrieveUser returned null - a violation of the interface contract" ) ; } try { this.preAuthenticationChecks.check ( user ) ; additionalAuthenticationChecks ( user, ( UsernamePasswordAuthenticationToken ) authentication ) ; } catch ( AuthenticationException ex ) { if ( !cacheWasUsed ) { throw ex; } // 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 ) ; this.preAuthenticationChecks.check ( user ) ; additionalAuthenticationChecks ( user, ( UsernamePasswordAuthenticationToken ) authentication ) ; } this.postAuthenticationChecks.check ( user ) ; if ( !cacheWasUsed ) { this.userCache.putUserInCache ( user ) ; } Object principalToReturn = user; if ( this.forcePrincipalAsString ) { principalToReturn = user.getUsername () ; } return createSuccessAuthentication ( principalToReturn, authentication, user ) ; }
代碼比較長,我們說要點:
代碼第 12 行,通過 retrieveUser
方法,獲得 UserDetails
信息,這個方法的具體實現,可以在 DaoAuthenticationProvider
中找到,主要是通過 UserDetailsService
的 loadUserByUsername
方法,查找系統中的用戶信息。
代碼第 25 行,通過 preAuthenticationChecks.check
方法,進行了認證前的一些校驗。校驗的具體實現可以在 DefaultPreAuthenticationChecks
內部類中找到,主要是判斷用戶是否鎖定、是否可用、是否過期。
代碼第 26 行,通過 additionalAuthenticationChecks
方法,對用戶名和密碼進行了校驗。具體實現可以在 DaoAuthenticationProvider
中找到。
代碼第 39 行,通過 postAuthenticationChecks.check
方法,校驗了密碼是否過期。具體實現可以在 DefaultPostAuthenticationChecks
內部類中找到。
最后,如果以上校驗和認證都沒有問題,則通過 createSuccessAuthentication
方法,創建成功的認證信息,并返回。此時,就成功通過了認證。
在最后的 createSuccessAuthentication
方法中,會創建一個新的 UsernamePasswordAuthenticationToken
認證信息,這個新的認證信息的認證狀態為 true
。表示這是一個已經通過的認證。
這個認證信息會返回到 UsernamePasswordAuthenticationFilter
中,并作為 attemptAuthentication
方法的結果。
在 doFilter
方法中,會根據認證成功或失敗的結果,調用相應的 Handler 類進行后續的處理,最后,認證的信息也會被保存在 SecurityContext 中,供后續使用。
以上就是“Spring Security認證的方法是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。