背景
目前比較流行的權(quán)限框架有Apache Shiro和 Spring Security。相比Spring Secrity, Shiro更加簡單,概念相對好理解。同時,原有項目對于用戶登錄態(tài),用戶權(quán)限的管理都使用到了Shiro,因此選擇Shiro作為一個權(quán)限框架的學習。Shiro讀音為[shee-roh],為日語的“城堡,堡壘”。本文將從兩個方面介紹。首先是Shiro的一些設(shè)計概念和原理,然后以一個比較簡單的Demo為例,介紹基于其實現(xiàn)的登錄和權(quán)限控制。閱讀時長大致45分鐘左右。
框架概念和結(jié)構(gòu)介紹
框架
參考:https://shiro.apache.org/architecture.html
簡單結(jié)構(gòu)

完整結(jié)構(gòu)

核心概念
Subject
表示為當前和軟件交互的主體。
SecurityManager
是整個框架的核心,協(xié)調(diào)多個功能組件一起發(fā)揮各自的作用,用于控制所有用戶的權(quán)限功能。
Realms
realms是將Shiro框架和應(yīng)用數(shù)據(jù)連接起來的橋梁,類似數(shù)據(jù)源。一般情況下,這些數(shù)據(jù)源存儲了用戶和權(quán)限定義。然后通過不同的realm實現(xiàn),和Shiro權(quán)限框架結(jié)合起來。Realms有多種提供的實現(xiàn),包括JDBC,LDAP,Text(INI)等,當然也可以實現(xiàn)自己的Realm,比如和自有的權(quán)限平臺連接起來。另外,Realms可以多個組合起來,實現(xiàn)多層次的授權(quán)處理。
其他重要概念
Authentication(認證)
用來處理用戶的認證過程,即登錄過程。主要是通過對用戶提供的賬號和密碼(或憑據(jù))校驗,判斷是否和保存的憑據(jù)匹配,匹配即認證通過,否則是認證失敗。
Authorization(授權(quán))
用來處理用戶權(quán)限的控制,用戶權(quán)限一般來說使用“角色-權(quán)限”的方式,即Role, Permission。不同用戶綁定了不同的角色,進而綁定了不同的權(quán)限。當然用戶也可以直接綁定到權(quán)限,但是一般不這么操作。然后權(quán)限和應(yīng)用程序的資源做了綁定,這樣實現(xiàn)了用戶所屬程序資源的控制。這里的資源,一般只某個接口,某個頁面,或某個方法。官方的文檔提供了如下的幾種權(quán)限樣例。
- 角色判斷
if ( subject.hasRole(“administrator”) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
- 權(quán)限判斷
注意這里使用A:B:C這種結(jié)構(gòu),其中的user表示角色,create表示一個操作
if ( subject.isPermitted(“user:create”) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
- 資源實例判斷
if ( subject.isPermitted(“user:delete:jsmith”) ) {
//delete the ‘jsmith’ user
} else {
//don’t delete ‘jsmith’
}
Session Management
Shiro框架提供Session管理,也支持使用數(shù)據(jù)庫等做持久化。容易理解,之前的Subject包括Authentication等概念中,用戶的判斷其實一定程度是依賴Session的。但是,當我們使用前后端分離的方式時或者某些不需要Session的場景,也可以關(guān)閉Shiro的HttpSession功能,更詳細的可以參考文后的資料。
Cryptography
這個模塊用于一些加密解密和哈希處理,Shiro對相關(guān)的算法做了封裝,能夠比較容易上手使用。
使用樣例
代碼介紹
代碼主體基于Springboot,然后集成Shiro,使用的依賴如下。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.9.1</version>
</dependency>
用戶憑據(jù)和權(quán)限定義文件
為了簡單起見,這里使用ini文件的方式來定義憑據(jù)和權(quán)限關(guān)系,實際也是一種Realms,是Shiro本身支持的方式,具體的實現(xiàn)邏輯在 org.apache.shiro.realm.text.IniRealm
其中[users]區(qū)域,定義用戶憑據(jù)和角色;[roles]區(qū)域定義了角色對應(yīng)的資源標記。其中“*”通配符可以實現(xiàn)權(quán)限的層次控制。如文件中描述,admin角色具有所有資源的權(quán)限,而author角色只有文章的創(chuàng)作和保存權(quán)限。
#shiro.ini
[users]
user = password, admin
user2 = password2, editor
user3 = password3, author
[roles]
admin = *
editor = articles:*
author = articles:compose,articles:save
接口附加權(quán)限的Controller
//authController.java
package com.auth.demo.controller;
import com.auth.demo.entity.AuthUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public Object login(@RequestBody AuthUser user) {
try {
AuthenticationToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
SecurityUtils.getSubject().login(token);
return "login";
} catch (Exception e) {
log.error("login failed:{}", user, e);
throw e;
}
}
@RequiresRoles("admin")
@GetMapping("/admin/op")
public Object adminOp() {
return "adminOp";
}
@RequiresPermissions("articles:*")
@GetMapping("/editor/op")
public Object editorOp() {
return "editorOp";
}
@RequiresPermissions("articles:compose")
@GetMapping("/author/compose")
public Object authorCompose() {
return "authorCompose";
}
@RequiresPermissions("articles:save")
@GetMapping("/author/save")
public Object authorSave() {
return "authorSave";
}
}
Shiro的相關(guān)配置
# used in develop and test
shiro:
web:
enabled: true
loginUrl: /auth/login
unauthorizedUrl:
登錄
通過調(diào)用login接口,執(zhí)行對應(yīng)的登錄操作。登錄成功返回對應(yīng)的數(shù)據(jù)。

失敗的情況

權(quán)限
用戶最小權(quán)限

最小權(quán)限用戶無中間角色權(quán)限

