Spring Boot 整合 Shiro 安全框架
同時整合 Druid 數據源、MyBatis 框架
整合 Druid 數據源是可選的
整合 MyBatis 框架只是為了登錄頁面展示數據庫的數據
三個框架並沒有依賴關系,按照任何順序整合都可以,也可以只整合Shiro
此章主要是關於 Shiro 的整合摸索,所以我將首先整合 Shiro
完整代碼和更多的文檔請移步Github springboot-05-shiro
建議對照觀看
Shiro 的基本功能和概念
此部分對應 shiro-quickstart
模塊
這是一個單獨的 Maven 項目,本質上就是 Shiro 官網的10分鍾快速上手
主要參考代碼是Github 樣例
也參考Shiro用starter方式優雅整合到SpringBoot中
因為 Shiro 屬於開源項目,所以文檔的維護並不是很及時與准確
一定要以最新版的源碼為准
必須了解的關於安全的概念
- 基於角色的訪問控制
- 基於權限的訪問控制
- 一個簡單樣例
這兩個概念不理解,后面Shiro的授權一定會雲里霧里
- Authentication:認證、鑒權
- Authorization:授權
與Spring Boot整合
值得一提的是,目前網上的很多整合教程都是導入shiro-spring
包
我們知道,這個shiro-spring
包本質上只是和Spring整合的包,導入之后我們還需要寫一個@Configuration
用以和Spring Boot整合
事實上,已經有shiro-spring-boot-web-starter
包發布,就像mybatis-spring-boot-starter
一樣,它已經幫助我們完成了部分配置
導入之后我們還需要進行少量的配置就可以了
需要我們配置的部分
由於安全策略與具體的業務會有聯系,就比如說不同的項目所含有的角色和權限定義是完全不同的,所以這部分必定是我們自己配置
並不存在完全自動配置的情況
參考 官網對Shiro和Spring Boot應用整合的說明
目前存在兩者需要我們配置的部分
Realm
這個詞的中文翻譯是領域
,Shiro在 前面的文檔 部分也對其做了解釋
我個人的理解是由於不同業務被認證和授權的主體不同,這里就是要定義這個主體是什么,以及如何對它進行授權和認證
Shiro 為我們提供了現成的Realm可以直接使用,本樣例中我使用類全限定名是org.apache.shiro.realm.jdbc.JdbcRealm
也就是從數據庫讀取用戶的信息 (用戶名、密碼、角色、權限),然后進行認證
在JdbcRealm
中定義了默認的查詢語句,我們根據查詢語句建立對應的數據庫表
protected static final String DEFAULT_AUTHENTICATION_QUERY="select password from users where username = ?";
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY="select password, password_salt from users where username = ?";
protected static final String DEFAULT_USER_ROLES_QUERY="select role_name from user_roles where username = ?";
protected static final String DEFAULT_PERMISSIONS_QUERY="select permission from roles_permissions where role_name = ?";
當然,JdbcRealm
是支持覆寫SQL語句的,你可以用set***Query
方法覆寫內置的SQL語句
不過這里對SQL語句查詢結果應該還會有其他要求,具體的我還沒有研究,我使用的就是默認的
同時還需要設置一個數據源
private DataSource dataSource;
@Autowired
public void setDataSource(DataSource dataSource){
this.dataSource=dataSource;
}
@Bean
public Realm realm(){
JdbcRealm realm=new JdbcRealm();
realm.setDataSource(dataSource);
// 基於權限的資源訪問默認是關閉的
realm.setPermissionsLookupEnabled(true);
return realm;
}
那么由於我使用的是自帶提供的JdbcRealm
,所以認證、授權的過程都不用我來具體實現,其還支持加鹽功能
ShiroFilterChainDefinition
定義完進行認證和授權的 Subject,下一步就是定義應該按照什么樣的規則進行認證和授權
Shiro框架支持通過URL或者注解配置認證和授權,這兩種方式沒有好壞之分,參考網上的說法,往往兩種結合才更有效
關於過濾器可以參考默認過濾器
這里貼出我寫的過濾規則
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition chainDefinition=new DefaultShiroFilterChainDefinition();
// 登出功能
chainDefinition.addPathDefinition("/logout","logout");
// 錯誤頁面無需認證
chainDefinition.addPathDefinition("/error","anon");
// druid連接池的角色控制,只有擁有admin角色的admin用戶可以訪問,不理解可以先不管
chainDefinition.addPathDefinition("/druid/**","authc, roles[admin]");
// 靜態資源無需認證
chainDefinition.addPathDefinition("/static/**","anon");
// 其余資源都需要認證
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
}
大體思想是在URL中配置認證規則 (鑒權),在Controller中使用注解配置授權,這樣的優勢:
- 減少代碼量,不必為每個Controller都寫上鑒權規則
- 利用注解可以細粒度設置基於角色或者基於資源的權限
基於角色的訪問控制
現在我們來看這一條規則
chainDefinition.addPathDefinition("/druid/**","authc, roles[admin]");
由於在配置完Shiro后,我為項目整合了 Druid
數據源,如果你對它不是很了解,建議去查看Github 文檔
這里無需對 Druid 有很詳細的了解,我想闡述的最關鍵的一點是,當你整合成功 Druid 框架,並且開啟了 Web 統計功能 (參考我的application.yml
)
訪問你的 項目URL/druid
會進入 Druid 數據源的管理界面,這里需要輸入你之前設置的用戶名和密碼才能查看項目對每一條執行過的SQL的統計情況
顯然這個功能我不希望普通角色的用戶也能查看,所以我將其設置為所有匹配規則 /druid/**
的URL都需要登錄並且需要有admin角色
對應的,你可以嘗試使用admin
用戶去登錄並訪問/druid
頁面,我已經將admin用戶設置為admin角色 (這里可能有一些繞,原諒我是為了省事才把兩個名字設置相同的,這里的用戶名完全可以修改為你自己的名字!)
之后,再嘗試使用andy67123
用戶去登錄並訪問/druid
頁面,它的角色僅僅是訪客
,看看會有什么不同
類似的,還有一處使用注解來控制角色訪問
在AccountInfoController
中,有這么一段代碼表明這處的資源需要admin角色才能訪問
@RequiresRoles("admin")
@RequestMapping("/account-info")
public String accountInfoTemplate(Model model){
...
}
基於權限的訪問控制
最后,我們來聊Shiro中常用功能同樣也很重要的一部分——Permissions
先上官方文檔 Understanding Permissions in Apache Shiro
這部分我也是想了很久,什么時候需要使用基於角色的訪問控制,什么時候需要使用基於資源的訪問控制,什么時候可以兩者合並使用
誠然,你的系統完全可以只使用基於角色的訪問控制,在此樣例中就只需要users
和user_roles
兩張表,然后在Controller使用@RequiresRoles("role")
注解去指定角色
也可以只使用基於權限的訪問控制 (依然需要角色,但是不再通過角色控制訪問),在此樣例中需要users
、user_roles
和roles_permissions
三張表,然后在Controller使用@RequiresPermissions("perm")
注解去指定權限
這一部分關於如何設計以及誰優誰劣先暫且不討論,因為我也不知道
此處先把 Shiro 的 Permissions 樣例展示一下
首先查看角色和權限對應的關系
# | 角色 | 權限 |
---|---|---|
1 | admin | manage:* |
2 | 訪客 | read:* |
3 | java開發 | read:* |
4 | java開發 | dev:java |
5 | python開發 | read:* |
6 | python開發 | dev:python |
也可以參考進入的/login
頁面提供的信息
- admin角色擁有所有的
管理權限
- 訪客只有
查看文檔的權限
- java開發可以
查看文檔
和進行java代碼開發
- python開發可以
查看文檔
和進行python代碼開發
再查看用戶和角色對應的關系
# | 用戶 | 角色 |
---|---|---|
1 | admin | admin |
2 | andy67123 | 訪客 |
3 | admin | java開發 |
4 | admin | python開發 |
5 | java | java開發 |
6 | python | python開發 |
- admin用戶擁有
admin
、java開發
和python開發
三種角色 - andy67123用戶擁有
訪客
角色 - java用戶擁有
java開發
角色 - python用戶擁有
python開發
角色
請試一試登錄完成后,在主頁點擊各個按鈕的結果,是否實現了各資源的權限控制