Spring Security極簡入門三部曲(上篇)

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將圍繞用戶表<->角色表展開(且將先不使用資料庫),資料庫的使用,以及角色表<->許可權表部分將在後續部落格講解

實現功能:

  1. 網站分為首頁、登陸頁、用戶頁、管理員頁、報錯頁
  2. 使用用戶名密碼登陸,登陸失敗報錯
  3. 首頁、登陸頁所有角色都能訪問
  4. 用戶頁需要USER或ADMIN許可權,管理員頁需要ADMIN許可權(許可權不足時跳轉至403頁面)
  5. 如果用戶沒有登錄,則訪問需要許可權的頁面時自動跳轉登錄頁面,登陸成功後自動跳轉至訪問的頁面
  6. 別愣著了,快把項目克隆到本地並嘗試運行呀!最好再自己新建一個項目跟著寫一遍~

測試結果:

  1. github項目地址,點擊主頁跳轉至登陸頁面,輸入定義好的用戶名和密碼
  2. 如果用戶名或密碼出錯則在登陸頁顯示錯誤
  3. 如果登陸成功則跳轉至/user(無論是用admin還是user登陸都是默認跳轉至/user)
  4. 如果在登陸狀態下用戶為user,此時點擊管理員頁的連接會跳轉至403(許可權不足),而點擊用戶頁則可以正常訪問
  5. 如果登陸狀態下用戶為admin,此時點擊管理員頁和用戶頁都可以正常訪問
  6. 點擊退出登陸則清除登陸賬戶session記錄,跳轉至/login登陸頁
  7. 如果一開始在未登錄時點擊用戶頁或管理員頁,則會先跳轉至登陸頁提示登陸,輸入資訊後會自動跳回之前請求的用戶頁或者管理員頁,但是,如果起初點擊的是管理員頁,而在提示輸入登陸資訊時輸入了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
}

小結

  1. 在本demo中,我們未用到資料庫去存放用戶和角色的資訊,這在極簡入門中篇中將會講解
  2. 此外我們也沒有在程式碼中模擬對於某項資源不同角色有不同的操作許可權,這在極簡入門的下篇將會講解
  3. 到此為止,你是否對用戶登陸成功後,session中用戶資訊的存放感到好奇?過濾器鏈的作用詳情又是是怎麼樣的?我們後面再做探討
  4. 歡迎評論區留言