Spring Security(1-2) UserDetailsService


UserDetailsService

#1. 基本概念

  • AuthenticationManager ,類似於 Shiro 中的 SecurityManager 。

    它是 “表面上” 的做認證和鑒權比對工作的那個人,它是認證和鑒權比對工作的起點。

    ProvierderManager 是 AuthenticationManager 的具體實現。

  • AuthenticationProvider ,類似於 Shiro 中的 Authenticator

    它是 “實際上” 的做認證和鑒權比對工作的那個人。從命名上很容易看出,Provider 受 ProviderManager 的管理,ProviderManager 調用 Provider 進行認證和鑒權的比對工作。

    我們最常用到 DaoAuthenticationProvider 是 AuthenticationProvider 的具體實現。

  • UserDetailService ,類似於 Shiro 中的 Realm 。

    雖然 AuthenticationProvider 負責進行用戶名和密碼的比對工作,但是它並不清楚用戶名和密碼的『標准答案』,而標准答案則是由 UserDetailService 來提供。簡單來說,UserDetailService 負責提供標准答案,以供 AuthenticationProvider 使用。

  • UserDetails,類似於 Shiro 中的 AuthenticationInfo + AuthorizationInfo

    UserDetails 它是存放用戶認證信息和權限信息的標准答案的 “容器” ,它也是 UserDetailService “應該” 返回的內容。和 Shiro 不同的是,Shiro 中是把認證和權限信息分開放的,而 UserDetails 則是塞到了一起。

  • PasswordEncoder,類似於 Shiro 中的 CredentialsMatcher 。

    Spring Security 要求密碼不能是明文,必須經過加密器加密。這樣,AuthenticationProvider 在做比對時,就必須知道『當初』密碼時使用哪種加密器加密的。所以,AuthenticationProvider 除了要向 UserDetailsService 『要』用戶名密碼的標准答案之外,它還需要知道配套的加密算法(加密器)是什么。

#2. UserDetailsService 和 UserDetails

之前有提到過,UserDetailsService 類似於 Shiro 中的 Realm,負責提供用戶信息的 “標准答案” ,供 AuthenticationProvider 來比對。Spring Security 提供了 2 個內置的 UserDetailsService 的實現類:InMemoryUserDetailsManager 和 JdbcDaoImpl 。不過在實際項目中,通常我們並不會使用到它倆。

非常別扭的一點是:從名字上,你根本看不出 InMemoryUserDetailsManager 和 JdbcDaoImpl 是 UserDetailsService 的實現類。

如果說 UserDetailsService 的職責類似於 Shiro 中的 Realm,那么 UserDetails 的職責就是 Shiro 中的 AuthenticationInfo + AuthorizationInfo 。

Spring Security 要求 UserDetailsService 將用戶信息的 “標准答案” 必須封裝到一個 UserDetails 對象中,返回給 AuthenticationProvider 使用(做比對工作)

我們可以直接使用 Spring Security 內置的 UserDetails 的實現類:User 。

public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 模擬注冊時的加密效果。 String password = NoOpPasswordEncoder.getInstance().encode("123"); // 硬編碼用戶名、密碼、角色權限信息。現實工作中並非如此。 return new User("tom", password, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN")); // 這里 admin 的大小寫有區別 } } 
Copied!

ProviderManager/AuthenticationProvider 在做密碼密碼的比對工作時,會調用 UserDetailsService 的 .loadUserByUsername() 方法,並傳入『用戶名』,用以查詢該用戶的密碼和權限信息。

UserDetails 中封裝了用戶登錄過程中所需的全部信息:

方法 說明
isAccountNonExpired
isAccountNonLocked
isCredentialsNonExpired
暫時用不到,統一返回 true ,否則 Spring Security 會認為賬號異常。
isEnabled 配合數據庫層面的邏輯刪除功能,用來表示當前用戶是否還存在、是否可用。
getPassword
getUsername
需要返回的內容顯而易見。
getAuthorities 用於返回用戶的權限信息。這里的權限就這是指用戶的角色。它的返回值類型是 Collection<? extends GrantedAuthority>,具體形式通常是:List<GrantedAuthority>,里面用來存儲角色信息(或權限信息)
return new User("tom", password, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN")); 
Copied!

SimpleGrantedAuthority 是 GrantedAuthority 的一個實現類,也是最常見最常用的和實現類。如果直接使用的話那就是 new SimpleGrantedAuthority("ROLE_USER") 。

注意

另外需要注意的一點是,在一套配置中如果你存在多個 UserDetailsService 的 Spring Bean將會影響 DaoAuthenticationProvider 的注入和使用,從而導致出現 No Provider ... 的異常。

#4. Spring Security 和 RBAC

雖然在 RBAC 模型中,用戶的 “權限” 是 “角色” 的下一級,但是在 Spring Security 中,它是將角色和權限一視同仁的,即,Spring Security 不強求你的角色和權限有上下級的關系。

在 Spring Security 中角色和權限都屬於 Authority 。不過,Spring Security 有個『人為約定』:

  • 如果你的 Authority 指的是角色,那么角色(的標准答案)就需要以 ROLE_ 開頭;

  • 如果你的 Authority 指的是權限,那么權限(的標准答案)則不需要特定的開頭。

在后續很多涉及『角色』的地方,Spring Security 都會對 ROLE_ 做額外處理。

#5. 配置使用自定義 UserDetailsService

@Slf4j @Configuration @SuppressWarnings("deprecation") public class SecurityConfig extends WebSecurityConfigurerAdapter { @Resource // 依賴注入 private MyUserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 這一步和 Shiro 中的 Realm 和 CredentialsMatcher 的綁定很像,讓兩者關聯。 auth.userDetailsService(userDetailsService) // 配置 UserDetailService .passwordEncoder(passwordEncoder()) // 配置 PasswordEncoder ; } } 
Copied!

#6. 最后的說明

這里有 2 點需要說明:

  • 截至目前為止,我們暫時只涉及到了『認證』,還沒有涉及到『鑒權』。

  • 截至目前為止,我們還有一些配置沒有自定義,仍然使用的是默認配置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM