SpringSecurity動態載入使用者角色許可權實現登入及鑑權
很多人覺得Spring Security實現登入驗證很難,我最開始學習的時候也這樣覺得。因為我好久都沒看懂我該怎麼樣將自己寫的用於接收使用者名稱密碼的Controller與Spring Security結合使用,這是一個先入為主的誤區。後來我搞懂了:根本不用你自己去寫Controller。你只需要告訴Spring Security使用者資訊、角色資訊、許可權資訊、登入頁是什麼?登陸成功頁是什麼?或者其他有關登入的一切資訊。具體的登入驗證邏輯它來幫你實現。
一、動態資料登入驗證的基礎知識
在本號之前的文章中,已經介紹了Spring Security的formLogin登入認證模式,RBAC的許可權控制管理模型,並且針對Spring Security的登入認證邏輯原始碼進行了解析等等。我們所有的使用者、角色、許可權資訊都是在配置檔案裡面寫死的,然而在實際的業務系統中,這些資訊通常是存放在RBAC許可權模型的資料庫表中的。下面我們來回顧一下其中的核心概念:
- RBAC的許可權模型可以從使用者獲取為使用者分配的一個或多個角色,從使用者的角色又可以獲取該角色的多種許可權。通過關聯查詢可以獲取某個使用者的角色資訊和許可權資訊。
- 在原始碼解析的文章中,我們知道如果我們不希望使用者、角色、許可權資訊寫死在配置裡面。我們應該實現UserDetails與UserDetailsService介面,從而從資料庫或者其他的儲存上動態的載入這些資訊。
以上是對一些核心的基礎知識的總結,如果您對這些知識還不是很清晰,建議您先往下讀本文。如果看完本文仍然理解困難,建議您翻看本號之前的文章。
二、UserDetails與UserDetailsService介面
- UserDetailsService介面有一個方法叫做loadUserByUsername,我們實現動態載入使用者、角色、許可權資訊就是通過實現該方法。函式見名知義:通過使用者名稱載入使用者。該方法的返回值就是UserDetails。
- UserDetails就是使用者資訊,即:使用者名稱、密碼、該使用者所具有的許可權。
下面我們來看一下UserDetails介面都有哪些方法。
public interface UserDetails extends Serializable { //獲取使用者的許可權集合 Collection<? extends GrantedAuthority> getAuthorities(); //獲取密碼 String getPassword(); //獲取使用者名稱 String getUsername(); //賬號是否沒過期 boolean isAccountNonExpired(); //賬號是否沒被鎖定 boolean isAccountNonLocked(); //密碼是否沒過期 boolean isCredentialsNonExpired(); //賬戶是否可用 boolean isEnabled(); }
現在,我們明白了,只要我們把這些資訊提供給Spring Security,Spring Security就知道怎麼做登入驗證了,根本不需要我們自己寫Controller實現登入驗證邏輯。
三、實現UserDetails 介面
public class SysUser implements UserDetails{
String password(); //密碼
String username(); //使用者名稱
boolean accountNonExpired; //是否沒過期
boolean accountNonLocked; //是否沒被鎖定
boolean credentialsNonExpired; //是否沒過期
boolean enabled; //賬號是否可用
Collection<? extends GrantedAuthority> authorities; //使用者的許可權集合
//省略構造方法
//省略set方法
//省略get方法(即介面UserDetails的方法)
}
我們就是寫了一個適應於UserDetails的java POJO類,所謂的 UserDetails介面實現就是一些get方法。get方法由Spring Security呼叫,我們通過set方法或建構函式為 Spring Security提供UserDetails資料。
四、實現UserDetailsService介面
@Component
public class MyUserDetailsService implements UserDetailsService{
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//這裡從資料庫sys_user表裡面查詢實體類物件。loadUser方法可使用Mybatis或JDBC或JPA自行實現。
SysUser sysUser = loadUser(username);
// 判斷使用者是否存在
if(user == null) { throw new UsernameNotFoundException("使用者名稱不存在"); }
//從資料庫該使用者所有的角色資訊,所有的許可權標誌
//遍歷所有的ROLE角色及所有的Authority許可權(選單、按鈕)。
//用逗號分隔他們的唯一標誌,具體過程自行實現。
sysUser.setAuthorities(
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_AMIN , system:user:delete"));
//sysUser.setAccountNonLocked(true或false);
return sysUser;
}
}
- 通常資料庫表sys_user欄位要和SysUser屬性一一對應,比如username、password、enabled。但是比如accountNonLocked欄位用於登入多次錯誤鎖定,但我們一般不會在表裡存是否鎖定,而是存一個鎖定時間欄位。通過鎖定時間是否大於當前時間判斷賬號是否鎖定,所以實現過程中可以靈活做判斷並用好set方法,不必拘泥於一一對應的形式。
- 角色是一種特殊的許可權,在Spring Security我們可以使用hasRole(角色標識)表示式判斷使用者是否具有某個角色,決定他是否可以做某個操作;通過hasAuthority(許可權標識)表示式判斷是否具有某個操作許可權。
五、最後說明
至此,我們將系統裡面的所有的使用者、角色、許可權資訊都通過UserDetailsService和UserDetails告知了Spring Security。但是多數朋友可能仍然不知道該怎樣實現登入的功能,其實剩下的事情很簡單了:
- 寫一個登入介面,寫一個登入表單,表單使用post方法提交到預設的/login路徑
- 表單的使用者名稱、密碼欄位名稱預設是username、password。
- 寫一個登入成功之後的跳轉頁面,比如index.html
然後把這些資訊通過配置方式告知Spring Security ,以上的配置資訊名稱都可以靈活修改。如果您不知道如何配置請參考本號之前的文章《formLogin登入認證模式》。
期待您的關注
- 向您推薦博主的系列文件:《手摸手教您學習SpringBoot系列-16章97節》
- 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格。