shiro RBAC基于角色的权限管理

1. 摘要

  1. RBAC简单理解为:谁扮演什么角色,被允许做什么操作。

    • 用户对象:user:当前操作用户
    • 角色对象:role: 表示权限操作许可权的集合
    • 权限对象:permission:资源操作许可权
  2. 例子:

    • 张三(user)下载(permission)一个高清无码的资源,需要VIP权限(role),
    • 张三——》普通用户——》VIP用户——》下载资源

2. 三种权限管理实现的方式

  1. 编程方式,使用if-else代码块完成

  2. 注解方式:使用@RequiresRoles(“admin”),需要有admin权限才可以执行

  3. jsp标签方式:

    <shiro:hasRole name="admin">
    	<!--执行满足权限的操作-->
    </shiro:hasRole>
    

3. 使用编程方式实现权限验证

  1. 首先,编写相应的配置文件

    [users]
    # 用户张三的密码是123,具有role1和role2两个权限
    zhangsan=123,role1,role2
    lisi=888,role2
    
    [roles]
    # 角色role1对资源user拥有create,update权限
    role1=user:create,user:update
    # 角色role2对资源user拥有create,delete权限
    role2=user:create,user:delete
    # 角色role3对资源user拥有create权限
    role3=user:create
    
    # ini权限配置规则:【用户=密码,角色1,角色n。】 【角色=权限1,权限2。】 首先根据用户名找角色,在根据角色找权限,角色是权限集合。
    
    # 权限字符串的规则是; 【资源标识符:操作:资源实例标识符】
    
    # 一般的,我们只需要关心 【资源:操作】
    # *:* 表示所有资源的所有操作。admin权限
    
  2. 角色验证测试代码

    package top.theonly.shiro.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UnknownAccountException;
    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 org.apache.shiro.util.ByteSource;
    import org.apache.shiro.util.Factory;
    import org.junit.Test;
    
    import java.util.Arrays;
    
    
    /**
     * Description:   这里的用户名和密码是指定在配置文件中的,硬编码方式
     * 测试 shiro 认证
     * <p>
     * 账号不存在抛出的异常
     * org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro.realm.text.IniRealm@1ff8b8f] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - zhangsan1, rememberMe=false].
     * <p>
     * 账号正常,密码错误抛出的异常
     * org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.
     **/
    public class ShiroTest {
    
        /**
         * 角色验证,判断用户是否拥有某个或某些角色
         * @throws Exception
         */
        @Test
        public void testHasRole() throws Exception {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "666");
            subject.login(token);
            // 进行授权操作时的前提,用户必须通过认证
            // 判断当前用户是否拥有某个角色,返回true表示拥有,false表示没有
            System.out.println(subject.hasRole("role1"));
            // 判断当前用户是否拥有一些角色,返回的Boolean类型的数组,顺序对应是否拥有权限
            System.out.println(Arrays.toString(subject.hasRoles(Arrays.asList("role1", "role2"))));
            // 判断用户是否拥有一些权限,返回一个Boolean值,
            System.out.println(subject.hasAllRoles(Arrays.asList("role1","role3")));
    
            // 如果有权限不做任何操作,没有权限则抛出下面的异常
            // org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role3]
            subject.checkRole("role3");
            
            // org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role3]
            subject.checkRoles("role1","role2","role3");
        }
    }
    
  3. 权限验证测试代码

    package top.theonly.shiro.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UnknownAccountException;
    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 org.apache.shiro.util.ByteSource;
    import org.apache.shiro.util.Factory;
    import org.junit.Test;
    
    import java.util.Arrays;
    
    
    /**
     * Description:   这里的用户名和密码是指定在配置文件中的,硬编码方式
     * 测试 shiro 认证
     * <p>
     * 账号不存在抛出的异常
     * org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro.realm.text.IniRealm@1ff8b8f] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - zhangsan1, rememberMe=false].
     * <p>
     * 账号正常,密码错误抛出的异常
     * org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.
     **/
    public class ShiroTest {
    
        /**
         * 判断用户是否拥有某个或某些权限
         * @throws Exception
         */
        @Test
        public void testHasPermission() throws Exception {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "666");
            subject.login(token);
    
            // 进行授权操作的前提,用户必须通过认证
            // 判断当前用户是否拥有某个权限,返回true表示拥有指定权限,false表示没有某个权限
            System.out.println(subject.isPermitted("user:delete"));  // true
    
            // 判断用户是否拥有一些权限, 返回true表示拥有这些权限,反之没有
            System.out.println(subject.isPermittedAll("user:list","user:create"));  // false
    
            // 判断用户是否拥有一些权限, 返回true表示拥有这些权限,反之没有
            System.out.println(Arrays.toString(subject.isPermitted("user:create","user:update")));  // [true, true]
    
            // 判断当前用户是否拥有某个权限,没有返回值,如果没有指定的权限将会抛出异常
            // org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [user:list]
            subject.checkPermission("user:list");
    
        }
    }
    

4. 自定义realm进行权限验证(解决权限验证硬编码)

  1. 配置自定义realm的配置文件

    [main]
    # 自定义realm,指定自己的realm类
    myRealm=top.theonly.shiro.realm.PermissionRealm
    # 指定securityManager的Realms实现
    securityManager.realms=$myRealm
    
  2. 编写自定义的PermissionRealm类

    package top.theonly.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 java.util.ArrayList;
    import java.util.List;
    
    public class PermissionRealm extends AuthorizingRealm {
    
        @Override
        public String getName(){
            return "PermissionRealm";
        }
    
        /**
         * 授权操作
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
            // 传入参数: principalCollection : 用户认证凭证信息
            // SimpleAuthorizationInfo: 认证方法返回封装认证信息中的第一个参数,用户信息
    
            // 当前登录的用户信息,用户认证
            String username = (String)principalCollection.getPrimaryPrincipal();
    
            // 模拟查询数据库,查询用户实现指定的角色,以及用户权限
            // 角色集合
            List<String> roles = new ArrayList<String>();
            // 权限集合
            List<String> permission = new ArrayList<String>();
            roles.add("role1");
    
            // 假设用户在数据库中拥有user:delete权限
            permission.add("user:delete");
    
            // 返回用户在数据库中的权限和角色
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    
            info.addRoles(roles);
            info.addStringPermissions(permission);
    
            return info;
        }
    
        /**
         * 认证操作
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
            // 通过用户名到数据库中查用户信息,封装成一个AuthenticationInfo对象返回,方便认证器进行对比。
            // 获取token中的用户名
            String username = (String) authenticationToken.getPrincipal();
    
            // 通过用户名查询数据库,将该用户对应查询结果,账号与密码
            // 这里的用户名和密码是查库查出来
            if(!"zhangsan".equals(username)){
                return null;
            }
    
            String password = "666";
    
            // info 对象表示realm登陆对比信息:
            // 参数1 : 用户信息(真实登陆中对应的user对象)
            // 参数2 :密码,
            // 参数3 : 当前realm的名字
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());
    
            return info;
        }
    }
    
    
  3. 测试自定义的权限realm类

    package top.theonly.shiro.test;
    
    import nu.shiro.realm.PermissionRealm;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UnknownAccountException;
    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 org.apache.shiro.util.ByteSource;
    import org.apache.shiro.util.Factory;
    import org.junit.Test;
    
    import java.util.Arrays;
    
    
    /**
     * Description:   这里的用户名和密码是指定在配置文件中的,硬编码方式
     * 测试 shiro 认证
     * <p>
     * 账号不存在抛出的异常
     * org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro.realm.text.IniRealm@1ff8b8f] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - zhangsan1, rememberMe=false].
     * <p>
     * 账号正常,密码错误抛出的异常
     * org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.
     **/
    public class ShiroTest {
    
        /**
         * 通过自定义realm验证用户是否拥有权限
         * 查看自定义的realm: {@link PermissionRealm}
         * @throws Exception
         */
        @Test
        public void testHasRoleByRealm() throws Exception {
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission-realm.ini");
            SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "666");
            subject.login(token);
    
            // 进行授权操作的前提,用户必须通过认证
            // 判断当前用户是否拥有某个权限,返回true表示拥有指定权限,false表示没有某个权限
            System.out.println(subject.isPermitted("user:delete"));  // true
    
            // 判断当前用户是否拥有某个角色
            System.out.println(subject.hasRole("role1"));  // true
    
        }
    
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值