轉自松哥:https://www.cnblogs.com/lenve/p/12321204.html
三個核心組件:Subject, SecurityManager 和 Realms.
Subject:即“當前操作用戶”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方進程、后台帳戶(Daemon Account)或其他類似事物。它僅僅意味着“當前跟軟件交互的東西”。但考慮到大多數目的和用途,你可以把它認為是Shiro的“用戶”概念。
Subject代表了當前用戶的安全操作,SecurityManager則管理所有用戶的安全操作。
subject.hasRole("") ;判斷是否有角色
subject.hasRoles(List);分別判斷用戶是否具有List中每個內容
subject.hasAllRoles();返回boolean,要求參數中所有角色用戶都需要具有
subject.isPermitted("");判斷是否具有權限
代碼演示https://www.cnblogs.com/xiaozhang666/p/12040122.html
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部組件實例,並通過它來提供安全管理的各種服務。
Realm:Realm 來實現認證(authentication)和/或授權(authorization)比如登錄時認證、登錄查詢成功后授權, Realm充當了Shiro與應用安全數據間的“橋梁”或者“連接器”。也就是說,當對用戶執行認證(登錄)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息,(詳細理解看代碼:Realm->SecurityManager->ShiroFilterFactoryBean->Subject)
登錄查詢成功后授權控制誰能訪問哪些資源(如訪問頁面/編輯數據/頁面操作等)。在授權中需了解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限(Permission)、角色(Role)
字符串通配符權限
規則:“資源標識符:操作:對象實例 ID” 即對哪個資源的哪個實例可以進行什么操作。其默認支持通配符權限字符串,“:”表示資源/操作/實例的分割;“,”表示操作的分割;“*”表示任意資源/操作/實例。
1、單個資源單個權限
subject().checkPermissions("system:user:update");
用戶擁有資源“system:user”的“update”權限。
2、單個資源多個權限
role41=system:user:update,system:user:delete
然后通過如下代碼判斷
subject().checkPermissions("system:user:update", "system:user:delete");
用戶擁有資源“system:user”的“update”和“delete”權限。
如上可以簡寫成:ini 配置(表示角色role42擁有 system:user 資源的 update 和 delete 權限)
role42="system:user:update,delete"
接着可以通過如下代碼判斷
subject().checkPermissions("system:user:update,delete");
通過“system:user:update,delete”驗證“system:user:update, system:user:delete”是沒問題的,但是反過來是規則不成立。
3、單個資源全部權限
ini 配置
role51="system:user:create,update,delete,view"
然后通過如下代碼判斷
subject().checkPermissions("system:user:create,delete,update:view");
用戶擁有資源“system:user”的“create”、“update”、“delete”和“view”所有權限。如上可以簡寫成:
ini 配置文件(表示角色 5 擁有 system:user 的所有權限)
role52=system:user:*
也可以簡寫為(推薦上邊的寫法):
role53=system:user
然后通過如下代碼判斷
subject().checkPermissions("system:user:*"); subject().checkPermissions("system:user");
通過“system:user:*”驗證“system:user:create,delete,update:view”可以,但是反過來是不成立的。
4、所有資源全部權限
ini 配置
role61=*:view
然后通過如下代碼判斷
subject().checkPermissions("user:view");
用戶擁有所有資源的“view”所有權限。假設判斷的權限是“"system:user:view”,那么需要“role5=::view”這樣寫才行
5、實例級別的權限
- 單個實例單個權限
ini 配置
role71=user:view:1
對資源 user 的 1 實例擁有 view 權限。
然后通過如下代碼判斷
subject().checkPermissions("user:view:1");
- 單個實例多個權限
ini 配置
role72="user:update,delete:1"
對資源 user 的 1 實例擁有 update、delete 權限。
然后通過如下代碼判斷
subject().checkPermissions("user:delete,update:1"); subject().checkPermissions("user:update:1", "user:delete:1");
- 單個實例所有權限
ini 配置
role73=user:*:1
對資源 user 的 1 實例擁有所有權限。
然后通過如下代碼判斷
subject().checkPermissions("user:update:1", "user:delete:1", "user:view:1");
- 所有實例單個權限
ini 配置
role74=user:auth:*
對資源 user 的 1 實例擁有所有權限。
然后通過如下代碼判斷
subject().checkPermissions("user:auth:1", "user:auth:2");
- 所有實例所有權限
ini 配置
role75=user:*:*
對資源 user 的 1 實例擁有所有權限。
然后通過如下代碼判斷
subject().checkPermissions("user:view:1", "user:auth:2");
Shiro 提供了 JSTL 標簽用於在 JSP/GSP 頁面進行權限控制,如根據登錄用戶顯示相應的頁面按鈕。
導入標簽庫
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
user 標簽 用戶已經身份驗證 / 記住我登錄后顯示相應的信息。
<shiro:guest> 歡迎游客訪問,<a href="${pageContext.request.contextPath}/login.jsp">登錄</a> </shiro:guest>
authenticated 標簽 用戶已經身份驗證通過,即 Subject.login 登錄成功,不是記住我登錄的。
<shiro:authenticated> 用戶[<shiro:principal/>]已身份驗證通過 </shiro:authenticated>
notAuthenticated 標簽 用戶已經身份驗證通過,即沒有調用 Subject.login 進行登錄,包括記住我自動登錄的也屬於未進行身份驗證。
<shiro:notAuthenticated> 未身份驗證(包括記住我) </shiro:notAuthenticated>
principal 標簽
<shiro: principal/>
顯示用戶身份信息,默認調用 Subject.getPrincipal() 獲取,即 Primary Principal。
<shiro:principal type="java.lang.String"/>
相當於 Subject.getPrincipals().oneByType(String.class)。
<shiro:principal type="java.lang.String"/>
相當於 Subject.getPrincipals().oneByType(String.class)。
<shiro:principal property="username"/>
相當於 ((User)Subject.getPrincipals()).getUsername()。
hasRole 標簽 如果當前 Subject 有角色將顯示 body 體內容。
<shiro:hasRole name="admin"> 用戶[<shiro:principal/>]擁有角色admin<br/> </shiro:hasRole>
hasAnyRoles 標簽 如果當前 Subject 有任意一個角色(或的關系)將顯示 body 體內容。
<shiro:hasAnyRoles name="admin,user"> 用戶[<shiro:principal/>]擁有角色admin或user<br/> </shiro:hasAnyRoles>
lacksRole 標簽 如果當前 Subject 沒有角色將顯示 body 體內容。
<shiro:lacksRole name="abc"> 用戶[<shiro:principal/>]沒有角色abc<br/> </shiro:lacksRole>
hasPermission 標簽 如果當前 Subject 有權限將顯示 body 體內容。
<shiro:hasPermission name="user:create"> 用戶[<shiro:principal/>]擁有權限user:create<br/> </shiro:hasPermission>
lacksPermission 標簽 如果當前 Subject 沒有權限將顯示 body 體內容。
<shiro:lacksPermission name="org:create"> 用戶[<shiro:principal/>]沒有權限org:create<br/> </shiro:lacksPermission>
Shiro 提供了使用thymeleaf模板整合了shiro標簽 - 界面可以直接使用。(此處簡單介紹兩個,更多請參考官方文檔)
<a href="#" shiro:hasPermission="system:user:add">包含權限字符串才能看到</a> <a href="#" shiro:hasRole="admin">管理員才能看到</a>
如果需要在JS中使用權限,使用封裝方法
// 驗證用戶是否具備某權限 var permission = [[${@permission.hasPermi('system:user:add')}]]; // 驗證用戶是否不具備某權限 var permission = [[${@permission.lacksPermi('system:user:add')}]]; // 驗證用戶是否具有以下任意一個權限 var permission = [[${@permission.hasPermi('system:user:add','system:user:edit')}]]; // 驗證用戶是否具備某角色 var role = [[${@permission.hasRole('admin')}]]; // 驗證用戶是否不具備某角色 var role = [[${@permission.lacksRole('admin')}]]; // 驗用用戶是否具有以下任意一個角色 var role = [[${@permission.hasAnyRoles('admin','common')}]]; // 驗證用戶是否認證通過或已記住的用戶 var isLogin = [[${@permission.isUser()}]]; // 追加標識可以實現隱藏 <a class="btn btn-success btn-xs ' + permission + '">包含權限字符串才能看到</a> <a class="btn btn-danger btn-xs ' + role + '">管理員才能看到</a>
————————————————
在 Spring Boot 中做權限管理,一般來說,主流的方案是 Spring Security ,但是,僅僅從技術角度來說,也可以使用 Shiro。
今天松哥就來和大家聊聊 Spring Boot 整合 Shiro 的話題!
一般來說,Spring Security 和 Shiro 的比較如下:
- Spring Security 是一個重量級的安全管理框架;Shiro 則是一個輕量級的安全管理框架
- Spring Security 概念復雜,配置繁瑣;Shiro 概念簡單、配置簡單
- Spring Security 功能強大;Shiro 功能簡單
- ...
雖然 Shiro 功能簡單,但是也能滿足大部分的業務場景。所以在傳統的 SSM 項目中,一般來說,可以整合 Shiro。
在 Spring Boot 中,由於 Spring Boot 官方提供了大量的非常方便的開箱即用的 Starter ,當然也提供了 Spring Security 的 Starter ,使得在 Spring Boot 中使用 Spring Security 變得更加容易,甚至只需要添加一個依賴就可以保護所有的接口,所以,如果是 Spring Boot 項目,一般選擇 Spring Security 。
這只是一個建議的組合,單純從技術上來說,無論怎么組合,都是沒有問題的。
在 Spring Boot 中整合 Shiro ,有兩種不同的方案:
- 第一種就是原封不動的,將 SSM 整合 Shiro 的配置用 Java 重寫一遍。
- 第二種就是使用 Shiro 官方提供的一個 Starter 來配置,但是,這個 Starter 並沒有簡化多少配置。
原生的整合
- 創建項目
創建一個 Spring Boot 項目,只需要添加 Web 依賴即可:

項目創建成功后,加入 Shiro 相關的依賴,完整的 pom.xml 文件中的依賴如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> </dependencies>
- 創建 Realm
接下來我們來自定義核心組件 Realm:
public class MyRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//授權? return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//認證! String username = (String) token.getPrincipal(); if (!"javaboy".equals(username)) { throw new UnknownAccountException("賬戶不存在!"); } return new SimpleAuthenticationInfo(username, "123", getName()); } }
在 Realm 中實現簡單的認證操作即可,不做授權,授權的具體寫法和 SSM 中的 Shiro 一樣,不贅述。這里的認證表示用戶名必須是 javaboy ,用戶密碼必須是 123 ,滿足這樣的條件,就能登錄成功!
- 配置 Shiro
接下來進行 Shiro 的配置:
@Configuration public class ShiroConfig { @Bean MyRealm myRealm() { return new MyRealm(); } @Bean SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } @Bean ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager()); bean.setLoginUrl("/login"); bean.setSuccessUrl("/index"); bean.setUnauthorizedUrl("/unauthorizedurl"); Map<String, String> map = new LinkedHashMap<>(); map.put("/doLogin", "anon"); map.put("/**", "authc"); bean.setFilterChainDefinitionMap(map); return bean; } }
在這里進行 Shiro 的配置主要配置 3 個 Bean :
- 首先需要提供一個 Realm 的實例。
- 需要配置一個 SecurityManager,在 SecurityManager 中配置 Realm。
- 配置一個 ShiroFilterFactoryBean ,在 ShiroFilterFactoryBean 中指定路徑攔截規則等。
- 配置登錄和測試接口。
其中,ShiroFilterFactoryBean 的配置稍微多一些,配置含義如下:
- setSecurityManager 表示指定 SecurityManager。
- setLoginUrl 表示指定登錄頁面。
- setSuccessUrl 表示指定登錄成功頁面。
- 接下來的 Map 中配置了路徑攔截規則,注意,要有序。
這些東西都配置完成后,接下來配置登錄 Controller:
@RestController public class LoginController { @PostMapping("/doLogin") public void doLogin(String username, String password) { Subject subject = SecurityUtils.getSubject(); try { subject.login(new UsernamePasswordToken(username, password)); System.out.println("登錄成功!"); } catch (AuthenticationException e) { e.printStackTrace(); System.out.println("登錄失敗!"); } } @GetMapping("/hello") public String hello() { return "hello"; } @GetMapping("/login") public String login() { return "please login!"; } }
測試時,首先訪問 /hello 接口,由於未登錄,所以會自動跳轉到 /login 接口:

然后調用 /doLogin 接口完成登錄:

再次訪問 /hello 接口,就可以成功訪問了:

使用 Shiro Starter
上面這種配置方式實際上相當於把 SSM 中的 XML 配置拿到 Spring Boot 中用 Java 代碼重新寫了一遍,除了這種方式之外,我們也可以直接使用 Shiro 官方提供的 Starter 。
- 創建工程,和上面的一樣
創建成功后,添加 shiro-spring-boot-web-starter ,這個依賴可以代替之前的 shiro-web 和 shiro-spring 兩個依賴,pom.xml 文件如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.4.0</version> </dependency> </dependencies>
- 創建 Realm
這里的 Realm 和前面的一樣,我就不再贅述。
- 配置 Shiro 基本信息
接下來在 application.properties 中配置 Shiro 的基本信息:
shiro.sessionManager.sessionIdCookieEnabled=true
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
shiro.unauthorizedUrl=/unauthorizedurl
shiro.web.enabled=true
shiro.successUrl=/index
shiro.loginUrl=/login
配置解釋:
- 第一行表示是否允許將sessionId 放到 cookie 中
- 第二行表示是否允許將 sessionId 放到 Url 地址攔中
- 第三行表示訪問未獲授權的頁面時,默認的跳轉路徑
- 第四行表示開啟 shiro
- 第五行表示登錄成功的跳轉頁面
- 第六行表示登錄頁面
- 配置 ShiroConfig
@Configuration public class ShiroConfig { @Bean MyRealm myRealm() { return new MyRealm(); } @Bean DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } @Bean ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition(); definition.addPathDefinition("/doLogin", "anon"); definition.addPathDefinition("/**", "authc"); return definition; } }
這里的配置和前面的比較像,但是不再需要 ShiroFilterFactoryBean 實例了,替代它的是 ShiroFilterChainDefinition ,在這里定義 Shiro 的路徑匹配規則即可。
這里定義完之后,接下來的登錄接口定義以及測試方法都和前面的一致,我就不再贅述了。大家可以參考上文。
總結
本文主要向大家介紹了 Spring Boot 整合 Shiro 的兩種方式,一種是傳統方式的 Java 版,另一種則是使用 Shiro 官方提供的 Starter,兩種方式,不知道大家有沒有學會呢?
