Shiro權限框架


1.Shiro是什么

  Shiro是一個非常強大的、易於使用的、開源的權限框架(安全框架)。它包括了權限校驗、權限授予、會話管理、安全加密等組件。

2.為什么需要使用Shiro

  在設計RBAC(Role Based Access Control)基礎系統時,需要編寫大量用於權限控制的代碼。如果使用Shiro就可以大大減少我們的工作量。因為Shiro已經將RBAC系統大量的代碼封裝好。
  如:頁面的顯示的HTML控件根據登錄用戶的權限不同而不同。使用Shiro可以輕松解決。

3.Shiro的下載

  shiro的下載路徑:http://shiro.apache.org/download.html

  Shiro包說明

  使用時根據列表的說明,下載需要的jar包即可

包 名 Maven 坐標 說 明
 shiro-all  不推薦采用  包含Shiro的所有功能 
 shiro-core
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
 只要使用shiro必須的核心包,它依賴slf4j 和 commons-beanutils 以及還需要一個INI配置文件
 shiro-web  <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
 支持基於Web的應用
 shiro-aspectj  <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>1.3.2</version>
</dependency>
 AspectJ對Shiro AOP和注釋的支持
 shiro-cas

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.3.2</version>
</dependency>

 對cas單點登錄框架的支持
 shiro-ehcache

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>

 對echche緩存框架的支持
 shiro-hazelcast

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-hazelcast</artifactId>
<version>1.3.2</version>
</dependency>

 對hazelcast的famework緩存的支持
 shiro-features
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-features</artifactId>
<version>1.3.2</version>
</dependency>
 Karaf 的集成
 shiro-guice

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-guice</artifactId>
<version>1.3.2</version>
</dependency>

 對谷歌的guice框架的支持(類似spring的ioc框架)
 shiro-quartz

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.3.2</version>
</dependency>

 對quartz定時任務調度框架的支持
 shiro-spring
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
 支持Spring框架集成。

        標紅部分為常用包

4.Shiro結構圖

  

Authentication:權限校驗,每次操作校驗用戶是否有訪問權限
Authorization:授權,用戶登錄時,授予用戶對應的權限
Session Management:會話管理,用於記錄用戶的登錄狀態
Cryptography:加密,加密算法的實現(SHA、MD5)
web Support:對Web項目的支持,Shiro的標簽!

5.Shiro入門

  5.1 訪問流程圖

    登錄流程:

  

 

 

1.應用訪問(如Web請求等)
2.Shiro根據訪問請求創建一個Subject對象來標識當前訪問的身份。
3.SecurityManger 加載配置文件進行身份校驗(驗證賬號密碼)
4.身份校驗通過后SecurityManger 會根據配置文件授予當前Subject對象對應的用戶權限

5.2 入門示例

配置步驟:

第一步:導入jar包

  

第二步:shiro.ini配置文件
創建一個shiro.ini配置文件,編寫權限認證信息。
注:1.shiro.ini文件名可以任意編寫,但后綴必須是ini
  2.shiro.ini配置文件放在classpath根目錄下

shiro.ini文件配置規則說明:

[main]   #用於配置SecurityManager里面的對象 
  對象名=類全限制名
  對象名.屬性[.屬性...] = 值 

[users]   #用於配置用戶名信息
   用戶名= 密碼, 角色1, 角色2, …, 角色N
 
[roles]   #用於配置角色信息
   角色名= 權限1, 權限2, …, 權限N   #全部權限使用 * (星號)
   
[urls]    #用於配置路徑攔截規則

  權限命名格式建議:權限:操作:操作

  shiro.ini配置:

##用戶信息
 [users]
 admin=123456,role_admin,role_user
 
 ##角色信息
 [roles]
 role_admin=*
 role_user=modular:to_add,modular:add

  測試:

package com.gjs.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

public class ShiroTest {
    public static void main(String[] args) {
        //第一步:讀取配置文件創建安全管理器
        IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.createInstance();
        //第二步:設置SecurityUtils的安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        
        //第三步:獲得一個沒有權限身份對象
        Subject subject = SecurityUtils.getSubject();
        
        //第四步:構建驗證信息token
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        
        //第四步:身份校驗(驗證賬號密碼)
        
        try {
            Subject resultSubject = securityManager.login(subject, token);
            
            System.out.println("校驗通過");
            System.out.println("用戶名:"+resultSubject.getPrincipal());
            System.out.println("驗證授權:"+resultSubject.isPermitted("modular:add"));
            
        } catch (AuthenticationException e) {
            System.out.println("校驗失敗,用戶名或者密碼不正確");
            e.printStackTrace();
        }
        
    }
}

 

API說明:

IniSecurityManagerFactory:作用加載ini配置文件獲得SecurityManagerFactory對象
SecurityManager:安全管理容器,就是否則整個Shiro框架授權校驗對象的管理
SecurityUtils :SecurityManager對象幫助類
Subject:驗證通過后用於存儲授權信息的身份對象
UsernamePasswordToken :用於設置校驗信息的對象
IncorrectCredentialsException :密碼出錯異常
UnknownAccountException:用戶名出錯異常

6.Realm的使用

  在入門示例中,用戶驗證信息來自於ini配置文,這樣的難以符合我們實際的需求。而在實際開發中,我們的用戶信息是存儲在數據庫里面,再從數據庫里面讀取出來。
Shiro是通過Realm機制,實現將配置文件的校驗用戶信息存放在數據庫等數據存儲系統里面。

  6.1 訪問流程圖

  

  如圖所示,我們需要在ini配置文件中配置Realm對象,再在Realm中進行權限驗證以及授權

6.2 示例:

配置步驟:

第一步:導入jar包

  第二步:編寫Realm

package com.gjs.shiro.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * 我們自定義Realm繼承授權的Realm AuthorizingRealm。因為授權包括校驗。
 * @author gjs
 *
 */
public class MyRealm extends AuthorizingRealm{
    /**
     * 權限校驗: 就是驗證訪問者(subject).是否使用有使用權限的身份,即驗證賬號密碼。驗證通過回AuthenticationInfo對象
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("權限校驗");
        System.out.println("用戶名:" + token.getPrincipal());
        if (token.getPrincipal().equals("admin")) {
            //參數1:用於存儲用戶信息,可以填充給Subject對象
            //參數2:校驗的密碼。注意Shiro的校驗是SimpleAuthenticationInfo內部完成的。
            //參數3:Realm名字,用來標識Realm
            return new SimpleAuthenticationInfo(token.getPrincipal(), "123456", this.getName());
        }
        return null;
    }
    /**
     * 權限授予:根據通過校驗的身份(subject)將查詢到的權限信息封裝在AuthorizationInfo里面返回
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("modular:add");//添加權限
        info.addRole("RoleAdmin");//添加角色
        return info;
    }
}

  第三步:創建shiro.ini配置文件

[main]
 ##聲明Realm對象
 myRealm=com.gjs.shiro.realm.MyRealm
 ##配置securityManager的realm對象。  對象引用需要在對象名前面加上 $
 securityManager.realms=$myRealm

  第四步:編寫測試類

package com.gjs.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

public class ShiroTest {
    public static void main(String[] args) {
        //第一步:讀取配置文件創建安全管理器
        IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.createInstance();
        //第二步:設置SecurityUtils的安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        
        //第三步:獲得一個沒有權限身份對象
        Subject subject = SecurityUtils.getSubject();
        
        //第四步:構建驗證信息token
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        
        //第四步:身份校驗(驗證賬號密碼)
        
        try {
            Subject resultSubject = securityManager.login(subject, token);
            
            System.out.println("校驗通過");
            System.out.println("用戶名:"+resultSubject.getPrincipal());
            System.out.println("驗證授權:"+resultSubject.isPermitted("modular:add"));
            System.out.println("是否有RoleAdmin角色:"+resultSubject.hasRole("RoleAdmin"));
            
        } catch (AuthenticationException e) {
            System.out.println("校驗失敗,用戶名或者密碼不正確");
            e.printStackTrace();
        }
        
    }
}

 

6.3 加密

  在開發中我們需要對密碼進行加密,而我們自己編寫的加密工具類無法傳遞給SimpleAuthenticationInfo對象,作為密碼校驗。所以就要用到shiro框架自帶的密碼加密的功能。
  SimpleHash類:用於生成指定的Hash算法。
  HashedCredentialsMatcher類:用於讓Realm校驗時,校驗指定的Hash算法
  ByteSource 用於給Hash算法加鹽的 

  示例:

  生成md5密碼

package com.gjs.shiro.test;

import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;

/**
 * 用來創建加密后的密碼,參數與校驗器的參數保持一致
 * @author gjs
 *
 */
public class Md5Util {
    public static void main(String[] args) {
        ByteSource salt = ByteSource.Util.bytes("gjs");
        Md5Hash md5=new Md5Hash("123456", salt, 3);
        String password = md5.toString();
        System.out.println(password);
    }
}

  修改ini配置文件

[main]
 ##聲明Realm對象
 myRealm=com.gjs.shiro.realm.MyRealm
 #加密的對象
 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
 ##指定加密算法. 屬性對應的是set方法
 credentialsMatcher.hashAlgorithmName=md5
 ##算法是否加鹽
 credentialsMatcher.hashSalted=true
 ##加密次數
 credentialsMatcher.hashIterations=3
 ##指定加密的校驗器給MyReam
 myRealm.credentialsMatcher=$credentialsMatcher
 ##配置securityManager的realm對象。  對象引用需要在對象名前面加上 $
 securityManager.realms=$myRealm

  修改Realm類:

package com.gjs.shiro.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * 我們自定義Realm繼承授權的Realm AuthorizingRealm。因為授權包括校驗。
 * @author gjs
 *
 */
public class MyRealm extends AuthorizingRealm{
    /**
     * 權限校驗: 就是驗證訪問者(subject).是否使用有使用權限的身份,即驗證賬號密碼。驗證通過回AuthenticationInfo對象
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("權限校驗");
        System.out.println("用戶名:" + token.getPrincipal());
        if (token.getPrincipal().equals("admin")) {
            ByteSource salt = ByteSource.Util.bytes("gjs");
            //參數1:用於存儲用戶信息,可以填充給Subject對象
            //參數2:校驗的密碼。注意Shiro的校驗是SimpleAuthenticationInfo內部完成的。
            //參數3:Realm名字,用來標識Realm
            return new SimpleAuthenticationInfo(token.getPrincipal(), "a0af233bfd499995a8c1bacc4f61c489",salt, this.getName());
        }
        return null;
    }
    /**
     * 權限授予:根據通過校驗的身份(subject)將查詢到的權限信息封裝在AuthorizationInfo里面返回
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("modular:add");//添加權限
        info.addRole("RoleAdmin");//添加角色
        return info;
    }
} 

  6.4 返回的認證信息為一個實體(JavaBean、Map)

  上面代碼校驗后返回的認證信息是一個字符串的用戶名,而我們如果將Shiro的校驗功能用到登錄的邏輯里面,明顯需要返回的不是一個用戶名,而是用戶的信息。
用戶的信息,我們需要用一個實體類來封裝。可以是JavaBean或者是Map
  我們上面寫的校驗方法返回的SimpleAuthenticationInfo的構建方法的第一個參數就是用於指定,返回的用戶認證信息的。可以將用戶名修改為一個我們指定的實體類對象就可以了

pojo實體類:

package com.gjs.shiro.pojo;

import java.util.Date;

public class User {
    private int id;
    private String name;
    private String password;
    private Date createDate;
    private int status;
    private Role role;
    
    public Role getRole() {
        return role;
    }
    public void setRole(Role role) {
        this.role = role;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Date getCreateDate() {
        return createDate;
    }
    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
    public int getStatus() {
        return status;
    }
    public void setStatus(int status) {
        this.status = status;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", password=" + password + ", createDate=" + createDate
                + ", status=" + status + ", role=" + role + "]";
    }
}
package com.gjs.shiro.pojo;

import java.util.List;

public class Role {
    private int roleId;
    private String roleName;
    private List<Perm> rolePerms;
    public int getRoleId() {
        return roleId;
    }
    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public List<Perm> getRolePerms() {
        return rolePerms;
    }
    public void setRolePerms(List<Perm> rolePerms) {
        this.rolePerms = rolePerms;
    }
    @Override
    public String toString() {
        return "Role [roleId=" + roleId + ", roleName=" + roleName + ", rolePerms=" + rolePerms + "]";
    }
    
}
package com.gjs.shiro.pojo;

public class Perm {
    private int permId;
    private String permName;
    private String permAction;
    private String permKey;
    public int getPermId() {
        return permId;
    }
    public void setPermId(int permId) {
        this.permId = permId;
    }
    public String getPermName() {
        return permName;
    }
    public void setPermName(String permName) {
        this.permName = permName;
    }
    public String getPermAction() {
        return permAction;
    }
    public void setPermAction(String permAction) {
        this.permAction = permAction;
    }
    public String getPermKey() {
        return permKey;
    }
    public void setPermKey(String permKey) {
        this.permKey = permKey;
    }
    @Override
    public String toString() {
        return "Perm [permId=" + permId + ", permName=" + permName + ", permAction=" + permAction + ", permKey="
                + permKey + "]";
    }
    
}

  修改Realm

package com.gjs.shiro.realm;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.gjs.shiro.pojo.Perm;
import com.gjs.shiro.pojo.Role;
import com.gjs.shiro.pojo.User;

/**
 * 我們自定義Realm繼承授權的Realm AuthorizingRealm。因為授權包括校驗。
 * @author gjs
 *
 */
public class MyRealm extends AuthorizingRealm{
    /**
     * 權限校驗: 就是驗證訪問者(subject).是否使用有使用權限的身份,即驗證賬號密碼。驗證通過回AuthenticationInfo對象
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("權限校驗");
           User user=new User(); //此處的數據應從數據庫查出來
        user.setId(1);
        user.setName((String)token.getPrincipal());
        user.setStatus(0);
        user.setCreateDate(new Date());
  
        if (token.getPrincipal().equals(user.getName())) {
            ByteSource salt = ByteSource.Util.bytes("gjs");
            //參數1:用於設置認證信息,返回給調用對象的
            //參數2:校驗的密碼。注意Shiro的校驗是SimpleAuthenticationInfo內部完成的。
            //參數3:密碼的鹽
            //參數4:Realm名字,用來標識Realm
            return new SimpleAuthenticationInfo(user, "a0af233bfd499995a8c1bacc4f61c489",salt, this.getName());
        }
        return null;
    }
    /**
     * 權限授予:根據通過校驗的身份(subject)將查詢到的權限信息封裝在AuthorizationInfo里面返回
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //獲取認證信息
        User user = (User) principals.getPrimaryPrincipal();
        //將授權信息也存到當前用戶對象里面(值傳遞)
        //角色
        Role role=new Role();
        role.setRoleId(1);
        role.setRoleName("RoleAdmin");
        user.setRole(role);
        //權限
        List<Perm> perms=new ArrayList<>();
        Perm perm1=new Perm();
        perm1.setPermId(1);
        perm1.setPermName("用戶管理");
        perm1.setPermAction("/user/toUserList");
        perm1.setPermKey("user:to_edit");
           
        perms.add(perm1);
                
        role.setRolePerms(perms);
        
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission(user.getRole().getRolePerms().get(0).getPermKey());//添加權限
        info.addRole(user.getRole().getName());//添加角色
        return info;
    }
}

  測試類:

package com.gjs.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;

import com.gjs.shiro.pojo.User;

public class ShiroTest {
    public static void main(String[] args) {
        //第一步:讀取配置文件創建安全管理器
        IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.createInstance();
        //第二步:設置SecurityUtils的安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        
        //第三步:獲得一個沒有權限身份對象
        Subject subject = SecurityUtils.getSubject();
        
        //第四步:構建驗證信息token
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        
        //第四步:身份校驗(驗證賬號密碼)
        
        try {
            Subject resultSubject = securityManager.login(subject, token);
            
            System.out.println("校驗通過");
            User user=(User) resultSubject.getPrincipal();//獲取認證信息
            System.out.println("用戶名:"+user.getName());
            System.out.println("驗證授權:"+resultSubject.isPermitted("modular:add"));
            System.out.println("是否有RoleAdmin角色:"+resultSubject.hasRole("RoleAdmin"));
            
            System.out.println("獲得角色:"+user.getRole());
            System.out.println("獲得第一個權限:"+user.getRole().getRolePerms().get(0));
        } catch (AuthenticationException e) {
            System.out.println("校驗失敗,用戶名或者密碼不正確");
            e.printStackTrace();
        }
        
    }
}

7.常用API:

IniSecurityManagerFactory : 用於加載配置文件,創建SecurityManager對象
SecurityManager :就是整個Shiro的控制對象
SecurityUtils :SecurityManager 工具類,用於獲得Subject對象
Subject :身份類,存儲返回的數據信息、提供了校驗的權限的方法
UsernamePasswordToken 身份信息構建類 (Token 令牌,作用就是傳入校驗參數)
AuthorizingRealm 支持校驗與授權的Realm
AuthenticationInfo 校驗成功返回的信息的父接口
SimpleAuthenticationInfo 校驗成功返回信息類
Md5Hash Md5加密類
ByteSource 字節碼處理工具類,我們在構造Md5加鹽時使用到。
HashedCredentialsMatcher Md5算法校驗器,用於支持Md5校驗
AuthorizationInfo 授權成功返回的信息類的父接口
PrincipalCollection 授予是獲得驗證信息的類
SimpleAuthorizationInfo 授權成功返回的信息類的實現類

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM