03-3 shiro授权

03-3 shiro授权

一、授权概述

  • 授权也称访问控制,应用中控制资源访问权限
  • 主体:访问应用的用户
  • 资源:应用中用户可以访问的任何数据
  • 权限:表示是否可以访问某个数据或资源
  • 角色:代表为操作集合,可以理解为权限的集合
  • 隐式角色:直接通过角色来验证用户是否有操作权限
  • 显式角色:通过权限控制用户访问资源
授权的三种方式:
  • 1、编程式:通过判断授权代码实现
Subject subject = SecurityUtils.getSubject();
boolean add = subject.hasRole("add");
  • 2、注解式:通过方法添加注解实现
@RequireRoles("add")
public void add(){
    //有权限操作
}
  • 3、jsp/Gsp标签:页面中通过标签实现
<shiro:hasRole name="add">
    //有权限操作
</shiro:hasRole>

二、授权验证

1、创建项目:01-role

2、添加配置文件:shiro_role.ini

  • 文件路径:resource文件夹下
[users]
zhang=123,role1,role2
wang=123,role1

3、授权校验

package com.role;
​
​
import org.apache.shiro.SecurityUtils;
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.Factory;
import org.junit.Test;
​
import java.util.Arrays;
​
/**
 * 授权验证
 *
 * @author brusion
 * @date 2018/9/15
 */
public class RoleApplication {
​
    @Test
    public void roleTest() {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_role.ini");
        SecurityManager manager = factory.getInstance();
​
        SecurityUtils.setSecurityManager(manager);
​
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
        try {
            subject.login(token);
            System.out.println("=== 登录成功 === ");
​
            boolean hasRole = subject.hasRole("role1");
            System.out.println("=== 是否有 role1 权限 === " + hasRole);
​
            boolean[] roles = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
            for (int x = 0; x < roles.length; x++) {
                System.out.println("=== 是否有 role" + x + " 权限 " + roles[x]);
            }
​
            boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));
            System.out.println("=== 是否同时拥有 role1,role2,role3 权限 === " + hasAllRoles);
​
        } catch (Exception e) {
            System.out.println("=== 登录失败 === ");
        }
        subject.logout();
    }
}
说明:
  • Subject#hasRole:单个权限验证,返回为boolean类型
  • Subject#hasRoles:多个权限验证,返回值为每个权限校验的数组
  • Subject#hasAllRoles:是否同时拥有多个权限,返回值为boolean类型
  • Subject#checkRole:同样有3个方法,使用基本一致,不同的是checkRole没有权限会抛出异常

三、对资源的控制

四、授权流程

4.1、授权流程
  • 1、调用Subject#isPermitted/hasRole接口,并委托给SecurityManager。而SecurityManager委托给Authorizer。(Authorizer是真正的授权者)
  • 2、Authorizer通过PermissionResolver吧字符串账号成对应的Permission实例。
  • 3、授权之前会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
  • 4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个会委托给ModularRealmAuthorizer进行循环判断。
4.2、ModularRealmAuthorizer多Realm匹配流程
  • 1、先检查相应的Realm是否实现了Authorizer
  • 2、如果实现了Authorizer调用相应的isPermitted/hasRole接口进行匹配
  • 3、如果有一个realm匹配将返回true,负责返回false

4.3、Realm授权流程

  • 1、如果调用hasRole,则直接获取AuthorizerInfo#getRoles与传入的角色比较即可
  • 2、如果调用如isPermitted(""),首先通过PermissionResolver将权限字符串转换成相应的实例,默认使用WildcardPermissionResolver,转换为通配符WildcardPermission
  • 3、通过AuthorizationInfo#getObjectPermissions得到Permission实例集合,通过通过获取用户的角色并通过RolePermissionResolver解析角色对应的权限。
  • 4、调用Permission#implies(Permission)逐个与传入的权限比较,匹配的权限返回true,负责返回false。

五、自定义RolePermissionResolver

5.1、创建项目:03-authorizer

5.2、创建类实现Permission定义权限规则

package com.authorizer.permission;
​
​
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.authz.Permission;
​
​
/**
 * 位移方式的权限
 * 规则
 * 权限字符串格式:+资源字符串+权限位+实例ID
 * 1、以+开头之间通过+分割
 * 2、权限:
 * 0 表示所有权限
 * 1 新增 0001
 * 2 修改 0010
 * 4 删除 0100
 * 8 查看 1000
 * 如 +user+10 表示对资源user拥有修改/查看权限
 * 不考虑一些异常情况
 *
 * @author brusion
 * @date 2018/9/16
 */
public class BitPermission implements Permission {
​
    private String resourceIdentify;
    private int permissionBit;
    private String instanceId;
​
    public BitPermission(String permissionString) {
        String[] array = permissionString.split("\\+");
​
        if (array.length > 1) {
            resourceIdentify = array[1];
        }
​
        if (StringUtils.isEmpty(resourceIdentify)) {
            resourceIdentify = "*";
        }
​
        if (array.length > 2) {
            permissionBit = Integer.valueOf(array[2]);
        }
​
        if (array.length > 3) {
            instanceId = array[3];
        }
​
        if (StringUtils.isEmpty(instanceId)) {
            instanceId = "*";
        }
​
    }
​
    @Override
    public boolean implies(Permission p) {
        //权限匹配判断
​
        if (!(p instanceof BitPermission)) {
            return false;
        }
        BitPermission other = (BitPermission) p;
​
        if (!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) {
            return false;
        }
​
        if (!(this.permissionBit == 0 || (this.permissionBit & other.permissionBit) != 0)) {
            return false;
        }
​
        if (!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) {
            return false;
        }
        return true;
    }
​
    @Override
    public String toString() {
        return "BitPermission{" +
                "resourceIdentify='" + resourceIdentify + '\'' +
                ", permissionBit=" + permissionBit +
                ", instanceId='" + instanceId + '\'' +
                '}';
    }
}

5.3、创建类实现权限解析

package com.authorizer.permission;
​
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
​
/**
 * @author brusion
 * @date 2018/9/16
 */
public class AuthorPermissionResolver implements PermissionResolver {
​
    @Override
    public Permission resolvePermission(String string) {
​
        //根据权限字符串是否以+开头来解析权限字符串为BitPermission或WildcardPermission
        if (string.startsWith("+")) {
            return  new BitPermission(string);
        }
        return new WildcardPermission(string);
    }
}

5.4、创建类实现权限集合

package com.authorizer.permission;
​
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.WildcardPermission;
​
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
​
/**
 * @author brusion
 * @date 2018/9/16
 */
public class AuthorRolePermission implements RolePermissionResolver {
​
    @Override
    public Collection<Permission> resolvePermissionsInRole(String string) {
​
        //根据角色字符串来解析得到的权限集合
        if ("role1".equals(string)) {
            List<Permission> permissions = Arrays.asList((Permission) new WildcardPermission("menu:*"));
            System.out.println("--- permissions权限集合是: " + permissions.toString());
            return permissions;
        }
        return null;
    }
}

5.5、创建类用于提供用户数据和用户权限

  • 推荐使用AuthorizingRealm代替Realm,只需要复写内部方法即可
package com.authorizer.realm;
​
import com.authorizer.permission.BitPermission;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
​
/**
 * @author brusion
 * @date 2018/9/16
 */
public class AuthorizerRealm extends AuthorizingRealm {
​
    //推荐使用AuthorizingRealm代替Realm,只需要复写内部方法即可
​
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
​
        //根据用户身份获取授权信息
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("role1");
        authorizationInfo.addRole("role2");
        authorizationInfo.addObjectPermission(new BitPermission("+user1+10"));
        authorizationInfo.addObjectPermission(new WildcardPermission("user1:*"));
        authorizationInfo.addStringPermission("+user2+10");
        authorizationInfo.addStringPermission("user2:*");
        return authorizationInfo;
    }
​
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
​
        //获取身份验证信息
        String username = (String) token.getPrincipal();  //得到用户名
        String password = new String((char[]) token.getCredentials()); //得到密码
        if (!"zhang".equals(username)) {
            throw new UnknownAccountException(); //如果用户名错误
        }
        if (!"123".equals(password)) {
            throw new IncorrectCredentialsException(); //如果密码错误
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo实现;
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}
​

5.6、创建ini配置文件:shiro_authorizer.ini

[main]
#自定义authorizer
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
#自定义permissionResolver
#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
permissionResolver=com.authorizer.permission.AuthorPermissionResolver
authorizer.permissionResolver=$permissionResolver
​
#自定义rolePermissionResolver
rolePermissionResolver=com.authorizer.permission.AuthorRolePermission
authorizer.rolePermissionResolver=$rolePermissionResolver
​
securityManager.authorizer=$authorizer
​
#自定义realm 一定要放在securityManager.authorizer赋值之后
#(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver)
realm=com.authorizer.realm.AuthorizerRealm
securityManager.realms=$realm

5.7、验证类

package com.authorizer;
​
import org.apache.shiro.SecurityUtils;
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.Factory;
import org.junit.Test;
​
/**
 * @author brusion
 * @date 2018/9/16
 */
public class AuthorizerApplication {
​
    @Test
    public void checkPermission() {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_authorizer.ini");
        SecurityManager manager = factory.getInstance();
​
        SecurityUtils.setSecurityManager(manager);
        Subject subject = SecurityUtils.getSubject();
​
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
        try {
            subject.login(token);
            System.out.println("  登录成功 ");
​
            //判断是否拥有权限
            boolean update1 = subject.isPermitted("user1:update");
            System.out.println("是否有权限 user1:update  " + update1);
            boolean update2 = subject.isPermitted("user2:update");
            System.out.println("是否有权限 user2:update  " + update2);
​
            //二进制方式权限判断
           System.out.println("是否有权限 +user1+1  " + subject.isPermitted("+user1+1"));
           System.out.println("是否有权限 +user1+8  " + subject.isPermitted("+user1+8"));
           System.out.println("是否有权限 +user2+10  " + subject.isPermitted("+user2+10"));
           System.out.println("是否有权限 +user1+4  " + subject.isPermitted("+user1+4"));
           System.out.println("是否有权限 menu:view  " + subject.isPermitted("menu:view"));
​
        } catch (Exception e) {
            System.out.println("  登录失败 " + e.toString());
        }
​
        subject.logout();
​
    }
}
​

5.8运行效果

  登录成功 
--- permissions权限集合是: [[menu]:[*]]
是否有权限 user1:update  true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 user2:update  true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+1  false
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+8  true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user2+10  true
--- permissions权限集合是: [[menu]:[*]]
是否有权限 +user1+4  false
--- permissions权限集合是: [[menu]:[*]]
是否有权限 menu:view  true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值