一、Shiro
爲什麼要用shiro:
1.項目中的密碼是否可以明文存儲?
2.是否任意訪客,無論是否登錄都可以訪問任何功能?
3.項目中的各種功能操作,是否是所有用戶都可以隨意使用?
綜上,當項目中的某些功能被使用時,需要進行安全校驗,進而保證整個系統的運行秩序。
1.1 Shiro是什麼
- Apache Shiro 是 Java 的一個安全(權限)框架。
- Shiro 可以輕鬆的完成:身份認證、授權、加密、會話管理等功能
- Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE 環境,也可以用在 JavaEE 環境。
- 功能強大且易用,可以快速輕鬆地保護任何應用程序 ( 從最小的移動應用程序到最大的Web和企業應用程序。)
- 方便的與Web 集成和搭建緩存。
- 下載:http://shiro.apache.org/
1.2 功能簡介
• 基本功能點如下圖所示:
• Authentication
身份認證/登錄,驗證用戶是不是擁有相應的身份;
• Authorization
授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能進行什麼操作
如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;
• Session Manager
會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有
信息都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;
• Cryptography
加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
• Web Support
Web 支持,可以非常容易的集成到Web 環境;
• Caching
緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;
• Remember Me
記住我,這個是非常常見的功能,即一次登錄後,下次再來的話可以立即知道你是哪個用戶
。。。。
二、Shiro 架構
2.1 工作流程
shiro 運行流程中,3個核心的組件:
Subject、SecutiryManager、Realm
shiro使用中,必須有的3個概念!!
• Subject
安全校驗中,最常見的問題是"當前用戶是誰?" "當前用戶是否有權做x事?",所以考慮安全校驗過程最自
然的方式就是基於當前用戶。Subject 代表了當前“用戶”,
應用代碼直接交互的對象是 Subject,只要得到Subject對象馬上可以做絕大多數的shiro操作。
也就是說 Shiro 的對外API 核心就是 Subject。
Subject 會將所有交互都會委託給 SecurityManager。
==Subject是安全管理中直接操作的對象==
• SecurityManager
安全管理器;即所有與安全有關的操作都會與SecurityManager 交互;
且其管理着所有 Subject;它是 Shiro的核心,
它負責與 Shiro 的其他組件進行交互,它相當於 SpringMVC 中DispatcherServlet 的角色
• Realm
Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說SecurityManager 要驗證用戶身份,那麼
它需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;
也需要從 Realm 得到用戶相應的角色/權限進行驗證用戶是否能進行操作;
可以把 Realm 看成 DAO,( 數據訪問入口 )
2.2 RBAC模型
Role Base Access Controll 基於角色的訪問控制
shiro採用的安全管理 模型
模型中3個主體:用戶、角色、權限
每個角色可以有多個權限,每個權限可以分配給多個角色
每個用戶可以有多個角色,每個角色可以分配給多個用戶
兩個多對多
則權限訪問控制,做的事是:
- 身份校驗:判斷是否爲合法用戶
- 權限校驗:用戶要做做某件事或使用某些資源,必須要擁有某角色,或必須擁有某權限
在訪問控制管理過程中,是要對項目中的資源(功能,數據,頁面元素等)的訪問、使用進行安全管理。
1> 首先照舊記錄用戶信息
2> 然後制定角色
3> 然後會對"資源"制定權限:即能對資源做的所有操作
4> 然後將權限分配給不同角色
5> 最後將角色分配給具體用戶
6> 如此完成對 用戶(李四,張三)使用某資源的訪問控制
2.3 架構
• Subject
任何可以與應用交互的“用戶”;
• SecurityManager
相當於SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;
所有具體的交互都通過 SecurityManager 進行控制;它管理着所有 Subject、且負責進行認證、授權、會話及
緩存的管理。
• Authenticator
負責 Subject 身份認證,是一個擴展點,可以自定義實現;可以使用認證
策略(Authentication Strategy),即什麼情況下算用戶認證通過了;
• Authorizer
授權器、即訪問控制器,用來決定主體是否有權限進行相應的操作;即控制着用戶能訪問應用中的哪些功能;
• Realm
可以有 1 個或多個 Realm,可以認爲是安全實體數據源,即用於獲取安全實體
的;可以是JDBC 實現,也可以是內存實現等等;由用戶提供;所以一般在應用中都需要
實現自己的 Realm;
• SessionManager
管理 Session 生命週期的組件;而 Shiro 並不僅僅可以用在 Web環境,也可以用在如普通的 JavaSE 環境
• CacheManager
緩存控制器,來管理如用戶、角色、權限等的緩存的;因爲這些數據基本上很少改變,
放到緩存中後可以提高訪問的性能
• Cryptography
密碼模塊,Shiro 提供了一些常見的加密組件用於如密碼加密/解密。
三、實際案例操作【重點】
這裏我們根據【RBAC】模型設計五張簡單的表,分別是用戶表、角色表、權限表和兩張中間表。
兩個用戶——李四是管理員,張三是普通用戶
兩個角色——管理員和普通用戶
四個權限——對信息的增刪改查
3.1 我們需要明確shiro在項目中的使用邏輯
3.2 導入相關的依賴
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--所有的spring項目都會依賴logging,所以必須導入-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
3.3 web.xml配置
xml文件配置完成後建議重啓服務器,不要重新部署
該配置必須放到所有工廠之前
<!--
1.項目的最外層構件訪問控制層
2.在項目啓動時初始化shiro環境,與spring和springmvc工廠一起啓動
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
在項目啓動時,加載web-info 或 classpath下的 shiro.ini ,並構建WebSecurityManager。
構建所有配置中使用的過濾器鏈(anon,authc等),ShiroFilter會獲取此過濾器鏈
-->
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
啓動Shiro的原理
xml配置文件中,通過EnvironmentLoaderListener啓動shiroFilter,實際上會經過以下幾個步驟
//1. 創建 "SecurityFactory",加載ini配置,並通過它創建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2. shiro核心:SecurityManager
SecurityManager securityManager = factory.getInstance();
//3. 託管到SecurityUtils工具類中(ops:之後可以不必關心SecurityManager)
SecurityUtils.setSecurityManager(securityManager);
這樣,外部只需要調取SecurityUtils中的subject方法,獲取subject對象即可,然後就可以進行各種判斷處理。
- 通過在ini中設置的 身份信息、角色、權限,做安全管理
- 1.身份認證: subject.login(token)
- 2.是否認證身份判斷:subject.isAuthenticated()
- 3.角色校驗:subject.hasRole(String:role); subject.hasRoles(List:roles)
- 4.權限校驗:subject.isPermitted(String:perm); subject.isPermittedAll(List:perms)
//4. 獲取subject,直接由用戶使用,調用功能簡單,其底層調用Securitymanager的相關流程
Subject subject = SecurityUtils.getSubject();
//登錄
//獲取token
UsernamePasswordToken token = new UsernamePasswordToken("lisi", "456");
//根據token登錄
subject.login(token);
//查看是否登錄
System.out.println("是否登錄?" + subject.isAuthenticated());
System.out.println("用戶憑證(用戶名) " + subject.getPrincipal());
//角色校驗
System.out.println("是否有管理員權限" + subject.hasRole("admin"));
System.out.println("是否有經理權限" + subject.hasRole("manager"));
//權限校驗
System.out.println("是否有對用戶查詢的權利?" + subject.isPermitted("user:query"));
// 登出:身份信息,登錄狀態信息,權限信息,角色信息,會話信息 全部抹除
subject.logout();
使用token登錄的時候,會發生異常,比如身份認證不正確、密碼不正確等等,可以使用【異常處理器】進行處理
// 身份認證( 類似登錄邏輯 )
if (!currentUser.isAuthenticated()) {//判斷是否已經登錄
//如果未登錄,則封裝一個token,其中包含 用戶名和密碼
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//將token傳入login方法,進行身份認證 (判斷用戶名和密碼是否正確)
currentUser.login(token);//如果失敗則會拋出異常
} catch (UnknownAccountException uae) {//用戶不存在
System.out.println("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {//密碼錯誤
System.out.println("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {//賬戶凍結
System.out.println("The account for username " + token.getPrincipal() + " is locked. "
+"Please contact your administrator to unlock it.");
}catch (AuthenticationException ae) {//其他認證異常
}
}
3.4 shiro.ini文件配置
這裏需要下載一個插件【Ini】
[main]
#沒有身份認證(是否登錄,有用戶名?)時,跳轉【handler】地址
shiro.loginUrl = /user/login
#角色或權限校驗不通過時,跳轉地址
shiro.unauthorizedUrl=/user/error
#登出後的跳轉地址,回首頁
shiro.redirectUrl=/
#聲明自定義的Realm
realm04=com.rj.realm.MyRealm
#將自定義的Realm註冊給 核心控制者:Securitymanager
securityManager.realms=$realm04
[urls]
/user/login = anon
/user/logout = logout
/user/all = authc,roles["admin"]
Ini配置文件分析
[urls]
# 如下格式:"訪問路徑 = 過濾器"
#【1.ant路徑:? * ** 細節如下】
# /user/login/page , /user/login/logic 是普通路徑
# /user/* 代表/user後還有一級任意路徑 : /user/a , /user/b , /user/c , /user/xxxxxxxxxxx
# /user/** 代表/user後還有任意多級任意路徑: /user/a , /user/a/b/c , /user/xxxx/xxxxxx/xxxxx
# /user/hello? 代表hello後還有一個任意字符: /user/helloa , /user/hellob , /user/hellox
#【2.過濾器,細節如下】
# anon => 不需要身份認證
# authc => 指定路徑的訪問,會驗證是否已經認證身份,如果沒有則會強制轉發到 最上面配置的loginUrl上
# ( ops:登錄邏輯本身千萬不要被認證攔截,否則無法登錄 )
# logout => 訪問指定的路徑,可以登出,【不用定義handler】。
# roles["manager","seller"] => 指定路徑的訪問需要subject有這兩個角色
# perms["user:update","user:delete"] => 指定路徑的訪問需要subject有這兩個權限
#其餘路徑都需要身份認證【用此路徑需謹慎】
/** = authc
#【3.注意】
# url的匹配,是從上到下匹配,一旦找到可以匹配的則停止,所以,通配範圍大的url要往後放,
# 如"/user/delete" 和 "/user/**"
其他默認過濾器
權限規則
1> user:query , user:insert , order:delete , menu:show
【:】 作爲分隔符,分隔資源和操作【資源:操作】
【,】 作爲分隔,分隔多個權限【權限1,權限2,權限3】
2> user:* , *:query
【*】 作爲通配符,代表所有操作、資源
【user:* 即user的所有操作】
【*:query 即所有資源的查詢操作】
3> *
【代表一切資源的一切權限 = 最高權限】
4> 細節:
1)【user:* 可以匹配 user:xx, user:xx:xxx】
【*:query 只可以匹配 xx:query,不能匹配 xx:xx:query. 除非 *:*:query】
2)【user:update,user:insert】 可以簡寫爲 【"user:update,insert"】
[roles]
manager1=user:query,user:update,user:insert
manager2="user:query,update,insert" #注意要加引號
#如上manager1和manager2權限等價
#subject.isPermittedAll("user:update","user:insert","user:query")
======================================================================================
1> user:update:1 , user:delete:1
# 對用戶1可以update,對用戶1可以delete
2> "user:update,delete:1" #和上面等價
# subject.isPermittedAll("user:update,delete:1","user:update:1","user:delete:1")
3> user:*:1 , user:update:* , user:*:*
3.5 建立pojo,建立dao並寫好映射文件
注意返回的角色名和權限名都是【set集合】
<!--用戶信息查詢-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rj.dao.UserDao">
<select id="findByUsername" resultType="User">
select * from user where username like #{username}
</select>
</mapper>
<!--三表聯查,查詢角色名稱-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rj.dao.RoleDao">
<select id="getRolenamesByUsername" resultType="string">
select r.rolename
from user u
inner join user_role ur
on u.uid = ur.uid
inner join role r
on ur.rid = r.rid
where u.username like #{username};
</select>
</mapper>
<!--五表聯查,查詢權限名稱-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rj.dao.PermissionDao">
<select id="getPerByUsername" resultType="string">
select p.pername
from user u
inner join user_role ur
on u.uid = ur.uid
inner join role r
on ur.rid = r.rid
inner join role_permission rp
on r.rid = rp.rid
inner join permission p
on rp.pid = p.pid
where u.username like #{username};
</select>
</mapper>
3.6 創建service層,然後定義自定義的Realm
Shiro中,主要就是依靠【SecurityManager】來實現,他會調用很多的資源,其中Realm是【角色權限】資源,這裏,我們需要自定義一個Realm,調用service來查詢相關數據,然後利用【標籤】或者【ini文件】進行比對過濾。
public class MyRealm extends AuthorizingRealm {
/**
* 作用:查詢權限信息,並返回即可,不用任何比對
* 何時觸發(標籤和ini比對時):/user/query = roles["admin"] /user/insert= perms["user:insert"]
* <shiro:hasRole/> <shiro:hasPermission/>
* 查詢方式:通過用戶名查詢,該用戶的 權限/角色 信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("在realm中 查詢權限");
//獲取用戶名
String username = (String) principals.getPrimaryPrincipal();
//查詢當前用戶的所有信息和權限
//由於Shiro並不是在Spring工廠裏的,所以這裏相當於是從工廠外部調取工廠內的東西,只能通過創建工廠獲得
RoleService roleService = (RoleService) ContextLoader.getCurrentWebApplicationContext().getBean("roleService");
PermissionService permissionService = (PermissionService) ContextLoader.getCurrentWebApplicationContext().getBean("permissionService");
Set<String> roles = roleService.getRolenameByUsername(username);
Set<String> perms = permissionService.getPernameByUsername(username);
//將查詢的信息封裝,然後返回結果
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(perms);
return simpleAuthorizationInfo;
}
/**
* 作用:查詢身份信息,並返回即可,不用任何比對
* 查詢方式:通過用戶名查詢用戶信息。
* 何時觸發:subject.login(token);登錄的時候會查詢身份信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("在Realm中查詢身份");
// 獲取用戶登錄時發送來的用戶名
String username = (String) token.getPrincipal();
//在數據庫中查詢信息
UserService userService = (UserService) ContextLoader.getCurrentWebApplicationContext().getBean("userService");
User user = userService.findByUsername(username);
//判斷是否存在,如果不存在返回null,該類會在後續直接拋出異常UnknownAccountException
if (user == null) {
return null;
}
// 將用戶信息封裝在 AuthenticationInfo 中
//this.getName()是標識,主要用於原子操作
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), this.getName());
}
}
Realm的職責
Realm的職責是,爲shiro加載 用戶,角色,權限數據,以供shiro內部校驗。
之前定義在ini中的數據,默認有IniRealm去加載。
現在庫中的數據,需要自定義Realm去加載。
ops : 沒必要在Realm中定義大量的查詢數據的代碼,可以爲Realm定義好查詢數據的DAO和Service。
Realm的父類,我們自定義的話需要繼承
如下是Realm接口的所有子類,其中IniRealm是默認的Realm,負責加載shiro.ini中的[users]和[roles]信息,當shiro需要用戶,角色,權限信息時,會通過IniRealm獲得。
自定義realm有兩個父類可以選擇:
1> 如果realm只負責做身份認證 ,則可以繼承:AuthenticatingRealm
2> 如果realm要負責身份認證和權限校驗,則可以繼承:AuthorizingRealm
3.7 創建controller
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login() {
System.out.println("轉到登錄頁面");
return "login";
}
@PostMapping("/login")
public String loginlogic(User user) {
System.out.println("進行登錄邏輯判斷");
//這裏調用了Shiro
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
subject.login(token);//登錄,走的是自定義的Realm的AuthenticationInfo方法,不一致則拋出異常由處理器處理
return "index";
}
@GetMapping("/all")
public String findAll() {
//在ini配置文件中,有【shiro.unauthorizedUrl=/user/error】,如果權限不足就會跳轉
System.out.println("查詢所有用戶,只有管理員權限纔可以查看");
return "modify";
}
@GetMapping("/error")
public String error() {
return "error";
}
@PutMapping("/modify")
public String modify(User user) {
System.out.println(user.getUsername());
return null;
}
}
3.8 創建異常解析器
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
ModelAndView mv = new ModelAndView();
//捕獲到如下的shiro拋出的認證異常,則重定向到一個handler處理
if (ex instanceof IncorrectCredentialsException
|| ex instanceof UnknownAccountException) {
//跳轉登錄頁面,重新登錄
mv.setViewName("redirect:/user/login");
}
return mv;
}
}
3.9 前端頁面,使用shiro標籤
shiro標籤的最大作用就是根據身份、角色和權限等進行判斷,然後將功能選擇性的展示給用戶
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--如果要使用shiro標籤,必須寫入路徑導入-->
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<html>
<body>
<h2>Hello World!</h2>
<h3>
<!--shiro標籤的最大作用就是根據身份、角色和權限等進行判斷,然後將功能選擇性的展示給用戶-->
<shiro:user> <!-- 常用,包含已登錄 且配合記住我,用戶體驗好 -->
歡迎您,<shiro:principal/>
<a href="${pageContext.request.contextPath}/user/logout">登出</a><br/>
<shiro:hasRole name="管理員">
<a href="${pageContext.request.contextPath}/user/all">查找所有人</a>
</shiro:hasRole>
<!--標籤會從MyRealm中去找,所以必須名稱與數據庫的名稱一致-->
<shiro:hasPermission name="刪除信息">
<a href="#">刪除用戶</a>
</shiro:hasPermission>
</shiro:user>
<shiro:guest>
歡迎您,未登錄,請<a href="${pageContext.request.contextPath}/user/login">登錄</a>
</shiro:guest>
</h3>
</body>
</html>
四、shiro標籤
shiro提供了很多標籤,用於在jsp中做安全校驗。
完成對頁面元素的訪問控制
4.1 導入shiro標籤庫
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<html>
....
</html>
4.2 身份認證
標籤 | 標籤含義 |
---|---|
<shiro:authenticated> |
已登錄 |
<shiro:user> |
已登錄或記住我(常用) |
<shiro:guest> |
遊客(未登錄且未記住我 常用) |
<shiro:notAuthenticated> |
未登錄 |
<shiro:principal> |
獲取用戶身份認證(用戶名) |
<shiro:authenticated>
歡迎您,<shiro:principal/>
</shiro:authenticated>
<shiro:user> <!-- 常用,包含已登錄 且配合記住我,用戶體驗好 -->
歡迎您,<shiro:principal/>
</shiro:user>
<shiro:guest>
歡迎您,未登錄,請<a href="<c:url value="/user/login/page"/>">登錄</a>
</shiro:guest>
<shiro:notAuthenticated>
您尚未登錄(記住我也算在未登錄中)
</shiro:notAuthenticated>
4.3 角色校驗
標籤 | 標籤含義 |
---|---|
<shiro:hasAnyRoles name="admin,manager"> |
是其中任何一種角色,多選,名稱必須和數據庫一致 |
<shiro:hasRole name="admin"> |
指定某一種角色,名稱必須和數據庫一致 |
<shiro:lacksRole name="admin"> |
不是指定角色,相當於是if-else中的else判斷 |
<table>
<tr>
<td>id</td>
<td>name</td>
<td>operation</td>
</tr>
<tr>
<td>001</td>
<td>張三</td>
<td>
<shiro:hasAnyRoles name="admin,manager">
<a href="#" style="text-decoration:none">詳情</a>
</shiro:hasAnyRoles>
<shiro:hasRole name="admin">
<a href="#" style="text-decoration: none">刪除</a>
</shiro:hasRole>
<shiro:lacksRole name="admin">
<a href="#" style="text-decoration: none">點擊升級</a>
</shiro:lacksRole>
</td>
</tr>
</table>
4.4 權限校驗
標籤 | 標籤含義 |
---|---|
<shiro:hasPermission name="user:delete"> |
有指定權限,注意,權限的名稱必須與數據庫名稱一致 |
<shiro:lacksPermission name="user:delete"> |
缺失指定權限,同樣必須和數據庫名稱一致 |
...
<td>
<a href="#" style="text-decoration:none">查看詳情</a>
<shiro:hasPermission name="user:delete">
<a href="#" style="text-decoration: none">刪除</a>
</shiro:hasPermission>
<shiro:lacksPermission name="user:delete">
<a href="#" style="text-decoration: none" >無權刪除</a>
</shiro:lacksPermission>
</td>
...
4.5 自定義標籤
jsp中允許自定義標籤,所以可以根據需求 自定義一些shiro標籤。
4.5.1 定義標籤類
public class MyAllRoleTag extends RoleTag {
// jsp中使用:<xxx:xx name="角色參數1,角色參數2,..."/>
private String name;//存儲傳入的角色參數
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
protected boolean showTagBody(String name) {
System.out.println("驗證角色:"+name);
String[] roles = name.split(",");
Subject subject = getSubject();
for(String role:roles) {
if(!subject.hasRole(role)){
return false;
}
}
return true;
}
}
4.5.2 定義tld文件
要放在
WEB-INF
目錄下,名稱任意
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.1.2</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>zhj</short-name>
<uri>http://zhj.apache.org/tags</uri>
<description>Apache Shiro JSP Tag Library.</description>
<tag>
<!-- 標籤名 <zhj:hasAllRoles .../> -->
<name>hasAllRoles</name>
<!-- 自定義Tag類路徑 -->
<tag-class>com.zhj.tag.MyAllRoleTag</tag-class>
<body-content>JSP</body-content>
<description>Displays body content only if the current Subject (user)
'has' (implies) all the specified roles
</description>
<!-- 自定義Tag中屬性名:name
<zhj:hasAllRoles name="role1,role2"/>
-->
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
4.5.3 使用
<%@ taglib prefix="zhj" uri="http://zhj.apache.org/tags" %>
<!-- tld中定義的標籤名:hasAllRoles, 向Tag類中傳遞參數:“seller,manager” -->
<zhj:hasAllRoles name="seller,manager">
有所有的角色2:manager,seller
</zhj:hasAllRoles>