【Shiro】3. Shiro授权流程

授权

授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。

授权的关键对象

授权可简单理解为Who对What / Which进行How操作。

  • Who:主体(Subject),主体需要访问系统中的资源。
  • What / Which:资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,比如商品信息为资源类型,类型为Type01的商品为资源实例,编号为001的商品信息也属于资源实例。
  • How:权限(Permission),规定了主体对资源的操作许可。权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

授权流程

授权一定是基于认证通过后才执行的操作。

20210927094653.png

授权方式(RBAC)

RBAC主要包含两种授权模式:

  • 基于角色的访问控制(Role-Based Access Control):以角色为中心进行访问控制。

    伪代码可表示为:

    if (subject.hasRole("admin")) {
        // 执行操作
    }
    
  • 基于资源的访问控制(Resource-Based Access Control):以资源为中心进行访问控制。

    伪代码可表示为:

    if (subject.isPermitted("user:find:*")) {
        // 执行操作
    }
    

    isPermission 传入参数是一个权限字符串,其格式为 "资源类型 : 操作 : 资源实例"

    " user : find : * " 表示的是Subject对所有User实例具有查询的权限(操作类型)。

    " user : * : 001 " 表示的是Subject对ID为001的User实例具有所有的权限(操作实例)。

Shiro权限字符串

1. 组成规则

在Shiro中使用权限字符串必须按照Shiro指定的规则。

权限字符串组合规则为:"资源类型标识符 : 操作 : 资源实例标识符"

  • 资源类型标识符: 一般会按模块,对系统划分资源。比如user模块,product模块,order模块等,对应的资源类型标识符就是:user,product,order。

  • 操作: 一般为增删改查(create,delete,update,find),还有 * 标识统配。

  • 资源实例标识符: 如果Subject控制的是资源类型,那么资源实例标识符就是 "*" ;如果Subject控制的是资源实例,那么就需要在资源实例标识符就是该资源的唯一标识(ID等)。

2. 示例

" * : * : * " 表示Subject对所有类型的所有实例有所有操作权限,相当于超级管理员。

" user : create : * " 表示Subject对user类型的所有实例有创建的权限,可以简写为:" user : create "。

" user : update : 001 " 表示Subject对ID为001的user实例有修改的权限。

" user : * : 001 " 表示Subject对ID为001的user实例有所有权限。

Shiro授权方式

Shiro中对于后台授权提供了三种实现方式:

  • 编程式
  • 注解式
  • 标签式(已淘汰,只能在JSP,Thymeleaf等模板引擎中使用)

1. 编程式

Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {
    // 有权限
} else {
    // 无权限
}

2. 注解式

@RequiresRoles("admin")
public void find() {
    // 有权限
}

Shiro授权源码

SimpleAccountRealm 类就是Shiro默认完成认证和授权的底层操作,其中 doGetAuthenticationInfo 方法处理认证 ,doGetAuthorizationInfo 方法处理授权。

public class SimpleAccountRealm extends AuthorizingRealm {
    
    ...
    
    // 认证处理
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    // 授权处理
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
    
}

Shiro授权Demo

授权必须在认证的基础上,因此本Demo基于前文中MD5加密认证的Demo实现授权。

1. 基于角色访问控制

在定义Realm中的授权操作中给Subject添加角色

public class UserMD5Realm extends AuthorizingRealm {

    // 授权操作
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取身份信息(用户名)
        String username = (String) principals.getPrimaryPrincipal();

        // 根据身份信息从数据库中该用户的角色信息装载进入SimpleAuthorizationInfo
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");

        // 返回权限信息对象
        return simpleAuthorizationInfo;
    }

    // 认证操作
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ...

        return null;
    }
}

授权流程

public static void main(String[] args) {
    // 认证操作
    ...

    // 如果认证成功,可以进行授权操作
    if (subject.isAuthenticated()) {

        // 验证Subject是否具有admin角色
        if (subject.hasRole("admin")) {
            // 具体授权操作
            System.out.println("具有 admin 角色");
        }

        // 验证Subject是否同时具有admin角色和user角色
        if (subject.hasAllRoles(Arrays.asList("admin", "user"))) {
            // 具体授权操作
            System.out.println("同时具有 admin 和 user 角色");
        }

        // 验证Subject是否具有以下角色中的一种或者多种
        List<String> roles = Arrays.asList("admin", "user");
        boolean[] hasRoles = subject.hasRoles(roles);
        for (int i = 0; i < roles.size(); i++) {
            if (hasRoles[i]) {
                // 具体授权操作
                System.out.println("具有 " + roles.get(i) + " 角色");
            }
        }
    }
}

2. 基于权限访问控制

在定义Realm中的授权操作中给Subject添加授权信息

public class UserMD5Realm extends AuthorizingRealm {

    // 授权操作
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取身份信息(用户名)
        String username = (String) principals.getPrimaryPrincipal();

        // 根据身份信息从数据库中该用户的权限信息装载进入SimpleAuthorizationInfo
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addStringPermission("user:create:001");
        simpleAuthorizationInfo.addStringPermission("user:update:*");

        // 返回权限信息对象
        return simpleAuthorizationInfo;
    }

    // 认证操作
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ...

        return null;
    }
}

授权流程

public static void main(String[] args) {
    // 认证操作
    ...

    // 如果认证成功,可以进行授权操作
    if (subject.isAuthenticated()) {

        // 判断Subject是否有创建所有user实例的权限
        if (subject.isPermitted("user:create:*")) {
            // 具体授权操作
            System.out.println("具有 user:create:* 权限");
        }

        // 判断Subject同时具有创建所有user实例的权限和修改001号user实例的权限
        if (subject.isPermittedAll("user:create:*", "user:update:001")) {
            // 具体授权操作
            System.out.println("同时具有 user:create:* 和 user:update:001 权限");
        }

        // 判断Subject是否具有以下权限中的一种或多种
        String[] permissions = {"user:create:*", "user:update:001"};
        boolean[] hasPermissions = subject.isPermitted(permissions);
        for (int i = 0; i < permissions.length; i++) {
            if (hasPermissions[i]) {
                // 具体授权操作
                System.out.println("具有 " + permissions[i] + " 权限");
            }
        }
    }
}


原文地址:https://juejin.cn/post/7012824575293521933

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值