Shiro基礎應用——角色和權限校驗

一、Shiro

爲什麼要用shiro:

1.項目中的密碼是否可以明文存儲?

2.是否任意訪客,無論是否登錄都可以訪問任何功能?

3.項目中的各種功能操作,是否是所有用戶都可以隨意使用?

綜上,當項目中的某些功能被使用時,需要進行安全校驗,進而保證整個系統的運行秩序。

1.1 Shiro是什麼

  • Apache Shiro 是 Java 的一個安全(權限)框架。
  • Shiro 可以輕鬆的完成:身份認證、授權、加密、會話管理等功能
  • Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE 環境,也可以用在 JavaEE 環境。
  • 功能強大且易用,可以快速輕鬆地保護任何應用程序 ( 從最小的移動應用程序到最大的Web和企業應用程序。)
  • 方便的與Web 集成和搭建緩存。
  • 下載:http://shiro.apache.org/

1.2 功能簡介

基本功能點如下圖所示:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rylpasMF-1570619762728)(mdpic\2.jpg)]

• Authentication
	身份認證/登錄,驗證用戶是不是擁有相應的身份;
• Authorization
	授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能進行什麼操作
	如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;
• Session Manager
	會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有
    信息都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;
• Cryptography
	加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
• Web Support
	Web 支持,可以非常容易的集成到Web 環境;
• Caching
	緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;
• Remember Me
	記住我,這個是非常常見的功能,即一次登錄後,下次再來的話可以立即知道你是哪個用戶
。。。。


二、Shiro 架構

2.1 工作流程

shiro 運行流程中,3個核心的組件:

Subject、SecutiryManager、Realm

shiro使用中,必須有的3個概念!!

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-brcCYF06-1570619762729)(mdpic\3.jpg)]

• 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個主體:用戶、角色、權限

每個角色可以有多個權限,每個權限可以分配給多個角色

每個用戶可以有多個角色,每個角色可以分配給多個用戶

兩個多對多

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-amQRDVg9-1570619762730)(mdpic/rbac1.jpg)]

則權限訪問控制,做的事是:

  1. 身份校驗:判斷是否爲合法用戶
  2. 權限校驗:用戶要做做某件事或使用某些資源,必須要擁有某角色,或必須擁有某權限

在訪問控制管理過程中,是要對項目中的資源(功能,數據,頁面元素等)的訪問、使用進行安全管理。

1> 首先照舊記錄用戶信息

2> 然後制定角色

3> 然後會對"資源"制定權限:即能對資源做的所有操作

4> 然後將權限分配給不同角色

5> 最後將角色分配給具體用戶

6> 如此完成對 用戶(李四,張三)使用某資源的訪問控制

2.3 架構

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vAw43P93-1570619762730)(mdpic\4.jpg)]

• 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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章