安全歸根節點需要解決兩個問題:
- 鑒權(authentication)--我是誰?
- 授權(authorization)--允許我做什么?
后者有些時候也被人們叫做“訪問控制”(access control)。spring security 是一個將鑒權和授權分開的安全框架。包含策略,同時提供對兩者的擴展能力。
Authencation Architecture
下圖描繪了spring security在鑒權處理流程中涉及到的類和過濾器。
- 外部的http請求訪問內部受保護的Resful API之前,需要經過一系列的安全過濾器。這些被spring security設定的過濾器,用於鑒權和授權的目的。當用戶的鑒權請求從外部進入時,將經過這條過濾器鏈,基於鑒權機制和模型,找到相應的Authentication Filter。比如說:
- HTTP Basic authentication request 經過“過濾鏈”,直到它到達BasicAuthenticationFilter 過濾器進行處理。
- HTTP Digest authentication request 經過“過濾鏈”,直到它到達DigestAuthenticationFilter 過濾器進行處理。
- login form authentication request 經過“過濾鏈”,直到它到達UsernamePasswordAuthenticationFilter過濾器進行處理。
- x509 authentication request 經過“過濾鏈”,直到它到達X509AuthenticationFilter 過濾器進行處理。
- 一旦鑒權請求被相應的Authentication Filter接收,該過濾器將會從接收的請求中提取出用戶名和密碼,基於這些用戶信息,創建一個 UsernamePasswordAuthenticationToken 對象,注意,該類實現了 Authentication 接口。
- UsernamePasswordAuthenticationToken 對象創建成功后,被用來作為 AuthenticationManager 中 authenticate() 方法的入參。 AuthenticationManager 僅僅是一個接口:
public interface AuthenticationManager{ Authentication authenticate(Authentication authentication)throws AuthenticationException; }
實際實現類是 ProviderManager 。該實現類包含了一系列配置的 AuthenticationProvider (s), 這些provider受ProvideManager委托,用於用戶請求的鑒權。
-
具體到實現細節, ProviderManager 會遍歷每個AuthenticationProvider,根據傳入的Authentication對象(例如: UsernamePasswordAuthenticationToken )嘗試鑒權用戶。AuthenticationProvider 接口如下所示:
public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class<?> authentication); }
這里是一些Spring security框架提供的AuthenticationProvider:
- CasAuthenticationProvider
- JaasAuthenticationProvider
- DaoAuthenticationProvider
- OpenIDAuthenticationProvider
- RememberMeAuthenticationProvider
- LdapAuthenticationProvider
- 有些AuthenticationProvider可能會使用 UserDetailsService ,用於獲取用戶的詳細信息。比如說 DaoAuthenticationProvider 可能需要根據傳入的用戶名從數據庫中獲取該用戶的詳細信息。
public interface UserDetailsService{ UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
-
UserDetailsService 返回 UserDetails 。而 User 是 UserDetails 的具體實現類,當然用戶也可以自行實現 UserDetails 接口。
- 同6
-
如果用戶鑒權成功,則返回Authentication對象,相比於傳入的未鑒權的對象,這個鑒權后的的對象更“豐滿”,包含了用戶的角色信息
從上圖可以看出,鑒權成功的Authentication對象(Fully populated Authentication Object)包含:
authenticated- true
grant authorities list :關聯的角色(角色和權限掛鈎)
user credentials:用戶憑證(僅僅包含用戶名)
如果鑒權未通過,則拋出異常 AuthenticationException 。該異常屬於運行時異常,不期望用戶通過try/catch去處理,spring security提供了一種通用的方式。即通過AuthenticationEntryPoint 處理:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler);
}
9. Authentication is done! 鑒權成功后,AuthenticationManager返回包含完整信息的鑒權對象給相關的Authentication Filter。
10. 將返回的鑒權對象保存到SecurityContext,用於后續過濾器的使用。比如Authorization Filters
SecurityContextHolder.getContext().setAuthentication(authentication);
Reference
https://springbootdev.com/2017/08/23/spring-security-authentication-architecture/