Spring Security極簡入門三部曲(上篇)
大家好,這里是白澤。最近學校里一個Spring Boot項目要用到Spring Security做安全相關的工作,好,沒用過,那我先學一下吧。查閱了不少文章,上來就是一堆接口,實現類,過濾器鏈,超級高水平,我看不懂。摸索了兩天終於有點感覺了,就寫一篇兩天前的我希望看到的博客吧
這篇博客能讓你初步理解Spring Security框架執行的過程,並用於你的Spring Boot項目中,但如果需要精進這個框架,還需要你閱讀更多源碼等后續的學習
寫在前面
-
代碼在我的github中:github項目地址,本篇博客對應名為spring-security-demo1的項目文件
-
學這個框架,不管你是跟着別人的博客學,還是看視頻學,不跟着寫個小的demo,想學明白需要點天賦
-
你需要會用Spring Boot,以及一些基本的前端知識(html/css/js),包括thymeleaf模板框架的基礎知識
-
MySQL數據庫相關知識,以及Spring Boot整合Mybatis框架的使用
為什么要用Spring Security
你也知道Spring Security有兩個功能,認證和授權,認證指的是驗證用戶名密碼是否正確(登陸),但你學習這個框架的初衷是想做授權,比如想控制你項目中有些資源(頁面)只有管理員能訪問,有些資源則普通用戶和管理員都能訪問。又或者針對同一個資源,用戶只有查看的權限,但是管理員有增、刪、改、查的權限。
盡管我很想一下子就告訴你這個框架的用法,讓你馬上cv到自己的項目里,但結合我的學習經歷,我認為,如果你剛開始接觸權限相關的知識,那么你很可能需要先梳理一下你自己項目的數據庫設計思路(因為用戶、角色、權限最終都是存在數據庫里的),所以別急,先往下看。
數據庫設計

為了實現不同角色訪問權限的控制,這里要用到至少5個表
用戶表和角色表是多對多的關系,用來表示每個用戶分別的是什么角色(如:A是用戶,B是管理員),二者的關系存放在用戶角色關系表中
角色表和權限表也是多對多的關系,用來表示每個角色有什么權限(如:對某資源,用戶有查看權限,管理員有增加、刪除、修改、查看權限),二者關系存放在角色權限關系表中
注意:在通篇博客中,如果我提到 ” 管理員有訪問XX資源(主要指頁面)的權限 “,這里的權限指的是訪問這個資源的資格(它只限制了角色);而如果我提到 ” 管理員對XX資源有增、刪、改、查的權限,而普通用戶只有查看權限 “,(它不僅限制了角色還細分了不同角色對同一個資源的操作權限)。而權限表中存放的是記錄是針對后者這種情況的(因此permission_code也可以理解為以某種操作處理某種資源的后端url是多少)
為了幫助理解,我擬了一張permission表(針對學生資源,有增、刪、改、查四個權限,所以在permission表中有四條記錄)

demo時刻
ps:spring-security-demo1將圍繞用戶表<->角色表展開(且將先不使用數據庫),數據庫的使用,以及角色表<->權限表部分將在后續博客講解
實現功能:
- 網站分為首頁、登陸頁、用戶頁、管理員頁、報錯頁
- 使用用戶名密碼登陸,登陸失敗報錯
- 首頁、登陸頁所有角色都能訪問
- 用戶頁需要USER或ADMIN權限,管理員頁需要ADMIN權限(權限不足時跳轉至403頁面)
- 如果用戶沒有登錄,則訪問需要權限的頁面時自動跳轉登錄頁面,登陸成功后自動跳轉至訪問的頁面
- 別愣着了,快把項目克隆到本地並嘗試運行呀!最好再自己新建一個項目跟着寫一遍~
測試結果:
- github項目地址,點擊主頁跳轉至登陸頁面,輸入定義好的用戶名和密碼
- 如果用戶名或密碼出錯則在登陸頁顯示錯誤
- 如果登陸成功則跳轉至/user(無論是用admin還是user登陸都是默認跳轉至/user)
- 如果在登陸狀態下用戶為user,此時點擊管理員頁的連接會跳轉至403(權限不足),而點擊用戶頁則可以正常訪問
- 如果登陸狀態下用戶為admin,此時點擊管理員頁和用戶頁都可以正常訪問
- 點擊退出登陸則清除登陸賬戶session記錄,跳轉至/login登陸頁
- 如果一開始在未登錄時點擊用戶頁或管理員頁,則會先跳轉至登陸頁提示登陸,輸入信息后會自動跳回之前請求的用戶頁或者管理員頁,但是,如果起初點擊的是管理員頁,而在提示輸入登陸信息時輸入了user的登陸信息,則在最終訪問管理員頁面時會出現403提示(因為user賬戶沒有admin權限)
核心代碼講解
事實上,需要你編寫的Spring Security相關代碼其實只涉及到一個類中的不到二十行代碼~,如果你只想知道怎么用,完全可以只看那個SecurityConfiguration配置類的代碼

這個自定義的配置類繼承了Spring Security提供的一個父類WebSecurityConfigurerAdapter,並重寫了它的兩個configure方法,第一個方法在程序運行時於內存中創建兩個用戶(用戶名&密碼)並賦予角色,我們前端的登陸就是去匹配這兩個定義好的用戶
user用戶,密碼為123(加密處理),賦予USER權限(角色)
admin用戶,密碼為123(加密處理),賦予USER和ADMIN權限(角色)
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 在內存中創建一個名為 "user" 的用戶,密碼為 "123",擁有 "USER" 權限,密碼使用BCryptPasswordEncoder加密
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER");
/**
* 在內存中創建一個名為 "admin" 的用戶,密碼為 "123",擁有 "USER" 和"ADMIN"權限
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123")).roles("USER","ADMIN");
}
第二個重寫的方法定義了訪問/user/路徑下的所有資源需要USER權限(角色),訪問/admin/路徑下的所有資源需要ADMIN權限(角色)
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login") //用戶未登錄時,訪問任何需要權限的資源都轉跳到該路徑,即登錄頁面,此時登陸成功后會繼續跳轉到第一次訪問的資源頁面(相當於被過濾了一下)
.defaultSuccessUrl("/user") //登錄認證成功后默認轉跳的路徑,意思時admin登錄后也跳轉到/user
.and()
.logout()
.logoutUrl("/logout") //退出登陸的路徑,指定spring security攔截的注銷url,退出功能是security提供的
.logoutSuccessUrl("/login");//用戶退出后要被重定向的url
}
小結
- 在本demo中,我們未用到數據庫去存放用戶和角色的信息,這在極簡入門中篇中將會講解
- 此外我們也沒有在代碼中模擬對於某項資源不同角色有不同的操作權限,這在極簡入門的下篇將會講解
- 到此為止,你是否對用戶登陸成功后,session中用戶信息的存放感到好奇?過濾器鏈的作用詳情又是是怎么樣的?我們后面再做探討
- 歡迎評論區留言
