【Shiro】三、Apache Shiro授权功能的主要流程

一、Shiro框架的授权功能简介

授权,也称为访问控制,是确定对应用程序中资源的访问权限的过程。换句话说,确定“谁有权访问什么”。授权用于回答安全性问题,例如“是否允许用户编辑帐户”,“是否允许该用户查看此网页”,“该用户是否有权访问此按钮?” 这些都是决定用户有权访问哪些内容的决定,因此,所有决定都代表授权检查。

授权是任何应用程序的关键要素,但它很快就会变得非常复杂。Shiro的目标是消除授权方面的许多复杂性,以便您可以更轻松地构建安全的软件。下面是Shiro授权功能的重点。

优点:

  • 基于主题的-您在Shiro中所做的几乎所有操作都基于当前正在执行的用户(称为主题)。而且,您可以轻松地访问主题以检索主题并在代码中的任何位置检查其角色,权限或其他相关属性。这使您可以更轻松地在您的应用程序中理解和使用Shiro。

  • 基于角色或权限的检查-由于应用程序之间授权的复杂性差异很大,因此Shiro的设计十分灵活,可根据项目需求同时支持基于角色的安全性和基于权限的安全性。

  • 强大而直观的权限语法-作为一种选择,Shiro提供了一种现成的权限语法,称为“通配符权限”,可帮助您对应用程序可能具有的细粒度访问策略进行建模。通过使用Shiro的通配符权限,您将获得易于处理且易于理解的语法。此外,您不必经历费时的工作和创建自己的方法来表示访问策略的复杂性。

  • 多种实施选项-Shiro中的授权检查可以通过代码内检查,JDK 1.5注释,AOP和JSP / GSP Taglibs完成。Shiro的目标是根据您的偏好和项目需求,让您选择使用您认为最佳的选项。

  • 强大的缓存支持-Shiro可以插入任何现代的开源和/或企业缓存产品,以提供快速有效的用户体验。对于授权而言,缓存对于使用后端安全性数据源在较大的环境中或更复杂的策略中的性能至关重要。

  • 可插拔数据源-Shiro使用可插拔数据访问对象(称为领域)连接到安全数据源,您可以在其中保存访问控制信息,例如LDAP服务器或关系数据库。为了帮助您避免自己构建和维护集成,Shiro为LDAP,Active Directory和JDBC等流行数据源提供了开箱即用的领域。如果需要,您还可以创建自己的领域,以支持基本领域中未包含的特定功能。

  • 支持任何数据模型-Shiro可以支持任何数据模型进行访问控制-不会对您施加任何模型。您的领域实现最终决定了如何将权限和角色组合在一起,以及是否对Shiro返回“是”或“否”的答案。通过此功能,您可以按照自己选择的方式来构建应用程序,而Shiro会竭尽全力为您提供支持。

二、授权中的几大核心概念

授权在Shiro中涉及了三个核心元素,即权限,角色和用户。

2.1 权限

权限是安全策略的最基本的级别,它们是功能的声明。权限代表可以在您的应用程序中执行的操作。格式正确的权限描述了资源类型以及与这些资源进行交互时可以执行的操作。你可以打开一个?你能一个文件?你可以删除一个客户记录?你能一个按钮吗?

数据相关资源的常见操作是创建,读取,更新和删除,通常称为CRUD。

重要的是要理解权限不知道可以执行这些操作,它们只是可以执行哪些操作的声明。

首先,权限指定对资源(门,文件,客户记录等)的操作(打开,读取,删除等)。在Shiro中,您可以定义任意深度的权限。以下是一些按粒度顺序排列的常见权限级别。

  • 资源级别-这是最广泛,最容易构建的资源。用户可以编辑客户记录或打开门。指定了资源,但未指定该资源的特定实例。
  • 实例级别-权限指定资源的实例。用户可以编辑IBM的客户记录或打开厨房门。
  • 属性级别-权限现在指定实例或资源的属性。用户可以编辑IBM客户记录上的地址。

2.2 角色

在授权的上下文中,角色实际上是权限的集合,用于简化权限和用户的管理。因此,可以为用户分配角色,而不是直接为他们分配权限,这会因更大的用户群和更复杂的应用程序而变得复杂。因此,例如,银行应用程序可能具有管理员角色或银行出纳员角色。

Shiro将支持这两种角色:隐式角色和显式角色

隐式角色

大多数人将角色视为我们定义为隐式角色的角色,其中您的应用程序暗含了一组权限,因为用户具有特定角色,而不是为角色明确分配权限或您的应用程序检查这些权限。代码中的角色检查通常反映了隐式角色。您可以查看患者数据,因为您具有管理员角色。您可以创建帐户,因为您具有银行出纳员角色。这些名称存在的事实与软件可以实际执行的操作没有任何关系。大多数人都以这种方式使用角色。这是最简单的方法,但是除了最简单的应用程序之外,它可能为其他所有程序带来很多维护和管理问题。

显式角色

显式角色具有显式分配的权限,因此是显式的权限集合。代码中的权限检查反映了显式角色。您可以查看患者数据,因为您拥有查看患者数据权限,这是管理员角色的一部分。您可以创建帐户,因为您在银行出纳员角色中具有创建帐户权限。您可以执行这些操作,不是因为有一些基于字符串的隐式角色名称,而是因为相应的权限已显式分配给您的角色。

显式角色的最大好处是易于管理,并减少了应用程序维护。如果您需要添加,删除或更改角色,则无需触摸源代码即可完成。在Shiro中,您还可以在运行时动态添加,删除或更改角色,并且您的授权检查将始终具有最新值。这意味着您不必强制用户注销并重新登录即可获取其新权限。

2.3 用户

用户是应用程序的“谁”。但是,在Shiro中,用户的概念实际上是Subject实例。我们使用“主题”一词来代替“用户”,因为用户通常暗指一个人,而在Shiro中,“主题”可以是与您的应用程序交互的任何东西-无论是人类还是服务。

允许用户通过与角色或直接权限的关联在您的应用程序中执行某些操作。因此,您可以打开客户记录,因为您已经通过分配的角色或通过直接权限分配获得了开放客户记录许可。

最终,您的Realm实现是与数据源(RDBMS,LDAP等)进行通信的对象。因此,您的境界将告诉Shiro是否存在角色或权限。您可以完全控制授权模型的工作方式。

三、授权的实现

Shiro中的授权可以通过三种方式处理:

  • 以编程方式-您可以在Java代码中使用诸如if和else块的结构执行授权检查。
  • JDK注释-您可以将授权注释附加到Java方法
  • JSP / GSP TagLibs-您可以根据角色和权限控制jsp或gsp页面的输出

3.1 编程方式授权

在Java代码中以编程方式检查权限和角色是处理授权的传统方法。这是您可以在Shiro中执行权限检查或角色检查的方法。

角色检查

这是如何在应用程序中以编程方式进行角色检查的示例。我们要检查用户是否具有管理员角色,如果他们具有管理员角色,那么我们将显示一个特殊按钮,否则将不显示它。

首先,我们可以访问当前用户Subject。然后,我们将管理员传递给主体的.hasRole()方法。它将返回TRUE或FALSE。

//get the current Subject 
Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {
    //show a special button‏
} else {
    //don’t show the button?)‏
}

现在,基于角色的检查可以快速,轻松地实现,但是它有一个主要缺点。它是隐式的。

如果您以后只想添加,删除或重新定义角色怎么办?您将不得不打开源代码并更改所有角色检查,以反映安全模型中的更改。您必须关闭应用程序,打开代码,对其进行测试,然后每次重新启动。

在非常简单的应用程序中,这可能已经足够了,但对于大型应用程序,这可能是整个应用程序生命周期内的主要问题,并为您的软件带来大量维护成本。

权限检查

这是如何通过权限进行安全检查的示例。我们要检查用户是否有权打印到laserjet3000n,如果他们同意,那么我们将显示一个打印按钮,否则我们将不会显示它。这是实例级别权限或实例级别授权的示例。

再次,首先您可以访问当前用户Subject。然后,您构造一个Permission代表资源操作的对象或实例。在这种情况下,实例被命名printerPermission,资源为laserjet3000n,操作为print。然后我们传递printerPermission给主体的.isPermitted()方法。它将返回true或false。

Subject currentUser = SecurityUtils.getSubject();

Permission printPermission = new PrinterPermission("laserjet3000n","print");

If (currentUser.isPermitted(printPermission)) {
    //do one thing (show the print button?)‏
} else {
    //don’t show the button?
}

权限检查(基于字符串)

您还可以使用简单的字符串而不是权限类进行权限检查。

因此,如果您不想实现我们的权限接口,则只需传入一个String。在此示例中,我们向.isPermitted()方法传递了一个字符串,printer:print:LaserJet4400n

String perm = "printer:print:laserjet4400n";

if(currentUser.isPermitted(perm)){
    //show the print button?
} else {
    //don’t show the button?
}

只要Realm知道如何使用它,就可以按照所需的方式构造许可权字符串。在此示例中,我们使用Shiro的可选权限语法WildCardPermissions。WildCardPermissions功能强大且直观。

使用基于字符串的权限检查,您将获得与之前示例相同的功能。这样做的好处是,您不必强制实现权限接口,而是可以通过简单的字符串构造权限。缺点是您没有类型安全性,并且如果您需要更复杂的权限功能(超出其表示的范围),则需要基于权限接口实现自己的权限对象。

3.2 批注授权

如果您不想执行代码级授权检查,则也可以使用Java注释,在使用Java批注之前,您需要在应用程序中启用AOP支持。

权限检查

在此示例中,我们希望在用户account:create可以调用该openAccount方法之前检查该用户是否具有权限。如果这样做,则按预期方式调用该方法,否则,将引发异常。

像程序检查一样,您可以将Permission对象或带有此注释的简单字符串方法一起使用。

//Will throw an AuthorizationException if none
//of the caller’s roles imply the Account
//'create' permission
@RequiresPermissions("account:create")‏
public void openAccount( Account acct ) {
    //create the account
}

角色检查

在此示例中,我们希望在用户teller可以调用该openAccount方法之前检查该用户是否具有该角色。如果这样做,则按预期方式调用该方法,否则,将引发异常。

//Throws an AuthorizationException if the caller
//doesn’t have the ‘teller’ role:
@RequiresRoles( "teller" )
public void openAccount( Account acct ) {
    //do something in here that only a teller
    //should do
}

3.3JSP TagLib授权

对于基于JSP / GSP的Web应用程序,Shiro还提供了一个标记库供您使用。

在此示例中,我们将向具有users:manage权限的用户显示指向“管理用户”页面的链接。如果他们没有许可,那么我们将向他们显示一个好消息。

首先,我们需要将Shiro taglib添加到我们的Web应用程序中。接下来,我们添加<shiro:hasPermission>带有对users:manage的检查的标签。<shiro:hasPermission>如果用户具有我们要检查的权限,我们将在标签内放置我们要执行的代码。如果我们要在用户缺乏权限的情况下采取措施,那么我们还需要添加<shiro:lacksPermission>标签,再次检查users:manage。如果用户缺乏许可,我们要执行的任何代码都需要放在<shiro:lacksPermission>标签中。

<%@ taglib prefix="shiro" uri=http://shiro.apache.org/tags %>
<html>
<body>
    <shiro:hasPermission name="users:manage">
        <a href="manageUsers.jsp">
            Click here to manage users
        </a>
    </shiro:hasPermission>
    <shiro:lacksPermission name="users:manage">
        No user management for you!
    </shiro:lacksPermission>
</body>
</html>

当然,还有用于检查角色以及其他用户数据和状态的标签。

有关JSP / GSP标签的更多信息,请查看JSP标签库

四、授权的流程

 

 

流程如下:

  1. 首先调用 Subject.isPermitted*/hasRole*接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
  2. Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
  3. 在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
  4. Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted*/hasRole* 会返回 true,否则返回 false 表示授权失败。

ModularRealmAuthorizer 进行多 Realm 匹配流程:

  • 首先检查相应的 Realm 是否实现了实现了 Authorizer;
  • 如果实现了 Authorizer,那么接着调用其相应的 isPermitted*/hasRole* 接口进行匹配;
  • 如果有一个 Realm 匹配那么将返回 true,否则返回 false。

如果 Realm 进行授权的话,应该继承 AuthorizingRealm,其流程是:

  • 如果调用 hasRole*,则直接获取 AuthorizationInfo.getRoles() 与传入的角色比较即可;首先如果调用如 isPermitted(“user:view”),首先通过 PermissionResolver 将权限字符串转换成相应的 Permission 实例,默认使用 WildcardPermissionResolver,即转换为通配符的 WildcardPermission;
  • 通过 AuthorizationInfo.getObjectPermissions() 得到 Permission 实例集合;通过 AuthorizationInfo.getStringPermissions() 得到字符串集合并通过 PermissionResolver 解析为 Permission 实例;然后获取用户的角色,并通过 RolePermissionResolver 解析角色对应的权限集合(默认没有实现,可以自己提供);
  • 接着调用 Permission.implies(Permission p) 逐个与传入的权限比较,如果有匹配的则返回 true,否则 false。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值