【Apache Shiro】Authorization

Authorization

说说权限的一些东东,不是Authentication,是Authorization。

简单说就是access control即访问控制,控制用户对某个资源的访问。

比如说,是否可以查看某个页面、修改某个数据,甚至能不能看到某个按钮。

我们通常用三种元素进行授权操作,分别是:

·Permissions

这个在Shiro中代表粒度(granularity)最小的(原子性)的安全策略。

权限的粒度也可以再细化三个等级:

·资源:比如我可以对用户信息进行修改

·实例:我可以对用户A的信息进行修改

·属性:我可以对用户A的信息中的姓名进行修改

通常情况下,资源都支持CRUD操作。但是,每一种操作对一个资源有着不同的意义。所以我们尽力将权限的粒度做的小一些。

Permission仅仅声明对某个资源可以进行什么样的操作。

比如:能否看到“删除用户”按钮?能否浏览用户列表页面?

·Roles:

Role可以说是一系列动作的集合,通常将Role分配给User,User有没有操作权限归因于Role。

Role有两种类型,分别是隐式(implicity)和显示(explicity)。

·Implicity Roles:根据某个角色判断是否对资源有操作权限,粒度较粗。

·Explicity Roles:关注是否有进行该操作的权限,角色只是聚合了权限,用户拥有某角色。

也可以理解为基于角色的访问权限控制与基于资源的访问权限控制(总之我讨厌这种咬文嚼字的感觉,但我们又有必要有效地表达出我们的想法)。

一般更建议使用基于资源的访问权限控制。

·Users:

即操作的主体,和Subject是一样的概念。

可以根据角色或者权限决定是否允许用户执行某个操作。

当然,我们可以直接将权限分配给用户,也可以将权限分配给角色再将角色分配给用户。

或者我们也可以根据具体的需求再加一个层级。

权限判断主要有3方式,分别是:

·编码方式:

先说说基于角色的控制,相对基于资源的控制来得简单些。

关键是最后两行代码:

Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("King","t;stmdtkg");
currentUser.login(token);

currentUser.hasRole("admin");
currentUser.checkRole("admin");

用角色判断主要是两类方法,hasRole*checkRole*,前者返回boolean,后者抛出异常。

boolean hasRole(String roleIdentifier);

boolean[] hasRoles(List<String> roleIdentifiers);

boolean hasAllRoles(Collection<String> roleIdentifiers);


void checkRole(String roleIdentifier) throws AuthorizationException;

void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException;

void checkRoles(String... roleIdentifiers) throws AuthorizationException;


通常,基于资源的控制较基于角色的控制更加有效。

用资源判断也是两类方法,isPermitted*checkPermission*,前者返回boolean,后者抛出异常。

但与基于角色的方法不同的是,基于资源的方法的参数除了String也可以是org.apache.shiro.authz.Permission类型。Permission是一接口,自定义一个Permission只需要实现boolean implies(Permission p)。

如果想更准确地表达一个权限,或者想在权限执行时增加一些逻辑或访问一些资源则可以用Permission对象。

·注解方式:

在方法上面加上权限注解。

	@RequiresRoles({"admin","leader"})
	public void deleteUsers(){
		//...
	}

解释一下这五个annotation。

·@RequiresAuthentication:访问或者调用被注解的类或者方法时通过认证。

·@RequiresGuest:需要从未通过认证且没有被记住(Remember me)。

·@RequiresPremissions:需要特定的权限。

·@RequiresRoles:需要特定的角色。

·@RequiresUser:需要已通过认证

·页面标签:

我们可以根据权限去影响页面的显示

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<shiro:hasPermission name="user!delete.do">
    <a href="###">删除用户</a>
</shiro:hasPermission>

Authorization Sequence

图转自Shiro官网,但是这个步骤是在是太罗嗦了。

Step 1.

Subject实例(一般为DelegatingSubject)的权限验证方法被调用(也就是hasRole*,checkRole*,isPermitted*,checkPermission*这一系列)。

DelegatingSubject:

    public boolean isPermitted(String permission) {
        return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
    }

Step 2.

获取所有的principals并将权限验证的工作委托给securityManager。

AuthorizingSecurityManager:

    public boolean isPermitted(PrincipalCollection principals, String permissionString) {
        return this.authorizer.isPermitted(principals, permissionString);
    }

Step 3.

securityManager直接将工作委托给其内部的authorizer。

   public AuthorizingSecurityManager() {
        super();
        this.authorizer = new ModularRealmAuthorizer();
    }

默认为ModularRealmAuthorizer,ModularRealmAuthorizer支持与多个Realm进行交互。

ModularRealmAuthorizer循环检查每一个Realm是否实现了Authorizer,检查通过则调用该Realm的权限验证方法。

ModularRealmAuthorizer

    public boolean isPermitted(PrincipalCollection principals, String permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }

Step 4.

Realm的权限验证方法被调用,从权限信息中获取权限集合,循环调用其implies方法判断是否拥有权限。

(如图,部分Realm也实现了Authorizer。另外AuthorizingRealm的constructor中

this.permissionResolver = new WildcardPermissionResolver();)

如果传入的权限是以String形式表示,则需要一个resolvePermission的过程。

此处会用到PermissionResolver将字符串转为Permission实例。

如果Realm的权限验证方法出现异常,异常将作为AuthorizationException传至Subject的caller。

而随后的Realm的验证方法都不会得到执行。

如果Realm的权限验证方法返回boolean(比如hasRole*或者isPermitted*)并且其中一个返回true,剩余的Realm则全部短路。

    public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }

    private boolean isPermitted(Permission permission, AuthorizationInfo info) {
        Collection<Permission> perms = getPermissions(info);
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(permission)) {
                    return true;
                }
            }
        }
        return false;
    }

PermissionResolver

顺便说说这个PermissionResolver,主要用于将以String表示的权限转为Permission实例。

如果想定义一个PermissionResolver,我们只需要实现一个方法。

public interface PermissionResolver {

    /**
     * Resolves a Permission based on the given String representation.
     *
     * @param permissionString the String representation of a permission.
     * @return A Permission object that can be used internally to determine a subject‘s permissions.
     * @throws InvalidPermissionStringException
     *          if the permission string is not valid for this resolver.
     */
    Permission resolvePermission(String permissionString);

}

上面说过AuthorizingRealm(注意他下面还跟着一大票Realm)中默认使用的PermissionResolver实例为WildcardPermissionResolver。什么是WildcardPemission?

用String表述权限的时候,即使我用"看用户列表页"、"晚上跑楼梯"、"open a file"这种字符串来描述也是没有问题的。

但是他没有可以利用的规则,我们无法用某种规则去解释他(当然,有些情况下可能不需要解释)。

Shiro提供了更直观有力的表述语法——WildcardPermission。

比如我对某个资源有某些操作权限。

举个栗子,对用户有查看权限

user:query

不仅有查看权限,还有增加、修改和删除

user:query
user:edit
user:create
user:delete

也可以写成

"user:query,edit,create,delete"

如果对某个资源有所有操作权限,则:

user:*

或者也可以对所有资源拥有查看权限:

*:query

如果要表示仅对某资源的某实例有某权限,则

user:query:king

当然,"*"也适用于实例级别的权限

user:*:king

继续说说PermissionResolver。

当以String表述权限时,多数AuthorizingRealm的实现都会先将其转换为Permission实例后再进行权限检查逻辑。

权限检查并不是单纯的字符串比较。基于Permission对象的权限检查可以呈现更好的逻辑,比如wildcardPermission中如果包含"*"什么的就不是字符串比较那么简单了。因此,几乎所有的Realm都需要将String转为Permission对象。

在Realm进行权限验证工作的上一层,也就是Authorizer中如果传递一个String表述的权限过来,Realm则使用PermissionResolver将其转换为Permission并开始验证工作。

所有做权限验证的Realm都默认使用WildcardPermissionResovler实例。

可能我们有更厉害的权限String语法,而且想让所有的Realm都支持这个语法。

这个时候我们可以自己定义一个PermissionResolver并将其设置为全局PermissionResolver(global PermissionResolver)。

比如在.ini配置文件中:

globalPermissionResolver = com.foo.bar.authz.MyPermissionResolver
securityManager.authorizer.permissionResolver = $globalPermissionResolver

如果想配置一个全局PermissionResolver,每一个被注入的Realm都需要实现PermissionResolverAware接口。

当然,如果是集成AuthorizingRealm就不用想这些了,因为...

public abstract class AuthorizingRealm extends AuthenticatingRealm

        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware

当然,也可以使用下面的方法显示地指定一个PermissionResolver。

	public void setPermissionResolver(PermissionResolver permissionResolver) {
        this.permissionResolver = permissionResolver;
        applyPermissionResolverToRealms();
    }

或者在.ini中...

permissionResolver = com.foo.bar.authz.MyPermissionResolver

realm = com.foo.bar.realm.MyCustomRealm
realm.permissionResolver = $permissionResolver

相应地,RolePermissionResolver也是同理,只不过PermissionResolver是解析为Permission对象,而RolePermissionResolver是将角色String解析为Permission对象集合。

【Apache Shiro】Authorization

时间: 2024-10-17 09:55:48

【Apache Shiro】Authorization的相关文章

【Apache CXF】CXF对JAX-RS的支持

用CXF构建RESTful services有两种方式:·CXF对JAX-RS的实现.·使用JAX-WS Provider/Dispatch API.官网上还有Http Bindings方式,他需要做一些繁琐的工作去创建资源再映射到服务上,这种方式从2.6时已经被移除了.刚好我这里有几个工程都是用第一种方式实现的,在这里便主要记录一下spring+CXF构建RESTful service. 首先列举一下JAX-RS的一些常用注解.·@Path:指定资源的URI.·@Produces/@Consu

【Apache CXF】CXF对JAX-WS的支持

相关dependency,我使用的版本是2.7.11: <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org

【Apache Ant】ANT解析以及ANT在myEclipse中的使用

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 维基百科上对Ant的介绍: Apache Ant,是一个将软件编译.测试.部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发. 由Apache软件基金会所提供.默认情况下,它的buildfile(XML文件)名为build.xml.每一个buildfile含有一个<project>和至少一个预设的<target>,这些targets包含许多ta

【Apache大系】Apache服务器面面观

1. 先看看百度百科对Apache的解释: apache(Web服务器)_百度百科 2. apache服务器本质上说是一个TCP socket服务,socket模型如下: 3. 看一下 1)Apache中Httpd.conf详解 2)Apache性能监控 3)nginx和apache的比较 4)[好]关于Apache的25个初中级面试题 5)Apache与Nginx的优缺点比较 4.  一些命令 1)用 ps 来看 httpd 进程数# ps -ef | grep httpd | wc -l 2

【Apache Kafka】 Kafka简介及其基本原理

??对于大数据,我们要考虑的问题有很多,首先海量数据如何收集(如Flume),然后对于收集到的数据如何存储(典型的分布式文件系统HDFS.分布式数据库HBase.NoSQL数据库Redis),其次存储的数据不是存起来就没事了,要通过计算从中获取有用的信息,这就涉及到计算模型(典型的离线计算MapReduce.流式实时计算Storm.Spark),或者要从数据中挖掘信息,还需要相应的机器学习算法.在这些之上,还有一些各种各样的查询分析数据的工具(如Hive.Pig等).除此之外,要构建分布式应用还

【Apache Kafka】Kafka安装及简单示例

(一)Apache Kafka安装 1.安装环境与前提条件 ??安装环境:Ubuntu16.04 ??前提条件: ubuntu系统下安装好jdk 1.8以上版本,正确配置环境变量 ubuntu系统下安装好scala 2.11版本 安装ZooKeeper(注:kafka自带一个Zookeeper服务,如果不单独安装,也可以使用自带的ZK) 2.安装步骤 ??Apache基金会开源的这些软件基本上安装都比较方便,只需要下载.解压.配置环境变量三步即可完成,kafka也一样,官网选择对应版本下载后直接

【Apache学习】httpd2.4 版本下 https配置

httpd2.4 编译安装请参考:http://adelazhu.blog.51cto.com/9455045/1682264 其中httpd2.4预编译时已安装了--enable-ssl模块,所以不需要像httpd2.2 单独安装mod_ssl. ./configure --prefix=/usr/local/httpd24 --sysconfdir=/etc/httpd24 --enable-so --enable-ssl --enable-cgi --enable-rewrite --wi

【Apache学习】linux中基于ip、基于端口的虚拟主机

由于httpd服务核心主机和虚拟主机,两种方式水火不容,所以,要设置虚拟主机,首先需要关闭核心主机,即注释httpd主配文件中的 vim /etc/httpd/conf/httpd.conf 虚拟主机有三种工作模式: 基于IP 基于Port 基于Host 基于IP 实现如下要求的两台虚拟主机 使用的ip为192.168.56.169(虚拟机的ip),192.168.56.170(需要自己添加ip) 全局监听Listen 80 增加ip 物理机ping这两个ip 修改配置文件 按如下创建目录和内容

【Apache ZooKeeper】命令行zkCli.sh使用指南

ZooKeeper命令行 原文                   http://blog.csdn.net/ganglia/article/details/11606807 ZooKeeper客户端有C语言和Java两个版本. ZooKeeper的命令在/usr/lib/zookeeper/bin文件夹下. 运行Java版本的客户端使用bash zkCli.sh -server IP:port ,运行C语言版本的使用./cli_mt IP:port,下面介绍Java版本的,C语言版差不多. 查