Shiro权限框架

2019-08-09 00:00:00 框架 权限 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结构图

  《Shiro权限框架》

Authentication:权限校验,每次操作校验用户是否有访问权限
Authorization:授权,用户登录时,授予用户对应的权限
Session Management:会话管理,用于记录用户的登录状态
Cryptography:加密,加密算法的实现(SHA、MD5)
web Support:对Web项目的支持,Shiro的标签!

5.Shiro入门

  5.1 访问流程图

    登录流程:

  《Shiro权限框架》

 

 

1.应用访问(如Web请求等)
2.Shiro根据访问请求创建一个Subject对象来标识当前访问的身份。
3.SecurityManger 加载配置文件进行身份校验(验证账号密码)
4.身份校验通过后SecurityManger 会根据配置文件授予当前Subject对象对应的用户权限

5.2 入门示例

配置步骤:

第一步:导入jar包

《Shiro权限框架》

  

第二步: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 访问流程图

  《Shiro权限框架》

  如图所示,我们需要在ini配置文件中配置Realm对象,再在Realm中进行权限验证以及授权

6.2 示例:

配置步骤:

第一步:导入jar包

《Shiro权限框架》

  第二步:编写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 授权成功返回的信息类的实现类

 

相关文章