第四节 Shiro权限管理

一、授权的理解

        你是谁,你是谁决定了你的身份是什么,你的身份决定了你能干什么。

        这里牵扯出三种对象。

        用户对象user:当前操作的用户。

        角色对象role :表示一组 "权限操作许可权" 的集合。

        权限对象permission:资源操作许可权。

        例如,大宇(user)需要下载(permission)一个高清无码的种子,需要VIP权限(role)。所以,本次下载大致流程是先判断大宇是不是vip,然后再查看vip这种角色有没有下载权限。

二、在自定义Realm中使用授权

        首先必须强调的是:进行授权操作的前提:用户必须通过认证。

        在真实的项目中,角色与权限都存放在数据库中。为了快速上手,我们先创建一个自定义Realm,模拟它已经登录成功。直接返回一个登录验证凭证,告诉Shiro框架,我们从数据库中查询出来的密码是也是就是你输入的密码。所以,不管用户输入什么,本次登录验证都是通过的。

package shiro;

public class PermissionRealm extends AuthorizingRealm ...
    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
throws AuthenticationException {
        String yourInputUsername = (String) token.getPrincipal();
        String yourInputPassword = new String((char[]) token.getCredentials());
        //默认要被验证的密码就是用户输入的密码,所以用户输入什么密码都是对的
        String passwordFromDB = yourInputPassword;
        return new SimpleAuthenticationInfo(yourInputUsername, passwordFromDB, getName());
    }

        好了,接下来,我们要重写我们本小节的核心方法了。此方法的方法签名是:

   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) 

        此方法的传入的参数PrincipalCollection  principals,是一个包装对象,它表示"用户认证凭证信息"。包装的是谁呢?没错,就是认证doGetAuthenticationInfo()方法的返回值的第一个参数yourInputUsername。你可以通过这个包装对象的getPrimaryPrincipal()方法拿到此值。就像下面的代码展示的那样。

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String yourInputUsername = (String) principals.getPrimaryPrincipal();
        return null;
    }

        既然在Realm中获取到了你的用户名,即登录凭证,那么下一步显然就是去数据库中查询你这个人是什么角色,有什么样的权限。假设你是一线开发人员,你对于公司代码所拥有的权限有:增加code:insert、修改code:update。用下面代码展示一下上述的论述。

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String yourInputUsername = (String) principals.getPrimaryPrincipal();
        //构造一个授权凭证
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //通过你的用户名查询数据库,得到你的权限信息与角色信息。并存放到权限凭证中
        info.addRole(getYourRoleByUsernameFromDB(yourInputUsername));
        info.addStringPermissions(getYourPermissionByUsernameFromDB(yourInputUsername));
        //返回你的权限信息
        return info;
    }

    private String getYourRoleByUsernameFromDB(String username) {
        return "coder";
    }

    private List<String> getYourPermissionByUsernameFromDB(String username) {
        return Arrays.asList("code:insert", "code:update");
    }

三、验证你的权限信息

        OK,现在,你的权限信息已经被Shiro框架知晓了。下面我们就来使用Shiro的相关API来验证你的权限信息。

        Subject这个门面又出现了!没错,几乎所有的Shiro框架的入口,都是这个门面对象。

        第一步,先把我们的Realm配置到shiro.ini配置文件中。以后将不会使用这个shiro.ini文件,实际项目中会把shiro的相关配置使用Spring的配置文件集成。不过,你现在还不必关心那种配置方式,等到了与Spring集成的时候再说。

#shiro.ini 配置文件
[main]
#等号右边的shiro.PermissionRealm是包名+类名
myRealm=shiro.PermissionRealm
securityManager.realms=$myRealm

        Shiro的Subject门面为我们提供了两套验证角色与权限的API接口。一组是以has开头的,它返回Boolean类型,如果你有此权限,那么返回true,反之返回false。另外一组是以check开头的,无任何返回值。它在检查你的角色信息或者权限信息的时候,如果你有此权限或者角色信息,它将不做任何操作,反之,则抛出一个异常,以中断当前程序的运行。

        OK,知道了如何检查角色或者权限信息,让我们开始编写代码吧。

    @Test
    public void testPermissionRealm() {
        Subject subject = login("anyUsername", "anyPassword");
        //使用断言判断用户是否已经登录
        Assert.assertTrue(subject.isAuthenticated());
        //---------登录结束------------

        //---------检查当前用户的角色信息------------
        System.out.println(subject.hasRole("coder"));
        //---------如果当前用户有此角色,无返回值。若没有此权限,则抛 UnauthorizedException------------
        subject.checkRole("coder");
        //---------检查当前用户的权限信息------------
        System.out.println(subject.isPermitted("code:insert"));
        //---------如果当前用户有此权限,无返回值。若没有此权限,则抛 UnauthorizedException------------
        subject.checkPermissions("code:insert", "code:update");
    }

    private Subject login(String yourInputUsername, String yourInputPassword) {
        //读取配置文件
        Factory<SecurityManager> factory =
                new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = 
                 new UsernamePasswordToken(yourInputUsername, yourInputPassword);
        //调用自定义Realm的doGetAuthenticationInfo方法进行认证操作
        subject.login(token);
        //返回当前shiro环境的门面
        return subject;
    }
package shiro;

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 java.util.Arrays;
import java.util.List;

/**
 * @author jay.zhou
 * @date 2018/12/22
 * @time 13:53
 */
public class PermissionRealm extends AuthorizingRealm {
    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
throws AuthenticationException {
        String yourInputUsername = (String) token.getPrincipal();
        String yourInputPassword = new String((char[]) token.getCredentials());
        //默认要被验证的密码就是用户输入的密码,所以用户输入什么密码都是对的
        String passwordFromDB = yourInputPassword;
        return new SimpleAuthenticationInfo(yourInputUsername, passwordFromDB, getName());
    }

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String yourInputUsername = (String) principals.getPrimaryPrincipal();
        //构造一个授权凭证
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //通过你的用户名查询数据库,得到你的权限信息与角色信息
        info.addRole(getYourRoleByUsernameFromDB(yourInputUsername));
        info.addStringPermissions(getYourPermissionByUsernameFromDB(yourInputUsername));
        //返回你的权限信息
        return info;
    }

    private String getYourRoleByUsernameFromDB(String username) {
        return "coder";
    }

    private List<String> getYourPermissionByUsernameFromDB(String username) {
        return Arrays.asList("code:insert", "code:update");
    }
}

        运行结果:

  

四、源码下载

        本章节项目源码:点击我下载源码 

----------------------------------------------------分割线------------------------------------------------------- 

        下一篇:第五节 授权过程源代码追踪

        阅读更多:跟着大宇学Shiro目录贴

        

 

posted @ 2022-07-17 12:16  小大宇  阅读(473)  评论(0编辑  收藏  举报