Java Authorization Guide with Apache Shiro[译]

2017-09-25
Apache

授权或者说访问控制指的是为资源分配访问权限。换句话说就是谁能干什么。

举个授权的栗子:用户允许看网页、改数据、看按钮或者打印吗?这些都能决定用户能不能做什么。

Elements of Authorization

授权有三个我们在Shiro中使用很多的核心元素–权限、角色和用户。

Permissions Defined

权限是安全策略的最原子级别,它们是功能语句。权限就是在你的应用中能做什么。权限描述了资源类型以及当你与这些资源进行交互时可能采取的操作。你能开门吗?你能读取文件吗?你能删除客户记录吗?你能按按钮吗?

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

重要的是要明白,权限不是谁能执行的操作 - 它们只是可以执行哪些操作的语句。

Levels of permission granularity

以上权限都指定资源(门,文件,客户记录等)上的操作(打开,读取,删除等)。在Shiro,您可以定义任何您喜欢的粒度的权限。以下是粒度顺序的几个常见权限级别。

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

获取更多信息:Permissions Documentation

Roles Defined

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

有2中类型的角色你需要知道,Shiro同时也支持。

Implicit Roles(隐式的角色)

代码中的角色检查通常是隐含角色的反映。你可以查看患者数据,因为你具有管理员角色。你可以创建一个帐户,因为你有银行出纳员角色。事实上这些名字的存在和软件具体能做的没有任何关系。大多数人以这种方式使用角色。这是最简单的,但它可以为所有但最简单的应用程序造成大量的维护和管理问题。

Explicit Roles(显式的角色)

显式角色具有明确分配给它的权限,因此是明确的权限集合。代码中的权限检查反映了明确的角色。你可以查看患者的数据,因为你将患者数据视图视为管理员角色的一部分。你可以创建一个帐户,因为你有创建帐户权限作为你的银行柜员角色的一部分。你可以执行这些操作,而不是因为基于字符串的一些隐式角色名称,而是因为相应权限被明确分配给你的角色。

明确角色的最大好处是易于管理,降低了应用程序的维护。如果你需要添加,删除或更改角色,则可以在不触摸源代码的情况下执行此操作。在Shiro中,你还可以在运行时动态添加,删除或更改角色,并且你的授权检查将始终具有最新值。这意味着你不必强制用户注销并重新登录以获取他们的新权限。

Users Defined

用户就是谁在用这个应用程序。然而,在Shiro,用户的概念实际上是Subject实例。我们使用Subject这个词而不是用户,因为用户通常意味着人类,而在Shiro中,Subject可以与你的应用程序进行任何交互 - 无论是人类还是服务。

用户就是允许在你的应用程序中通过角色或者权限的关联执行某些动作。因此你可以打开一个客户的记录,因为你已经被分配了打开客户记录的权限,或者你被分配了带有权限的角色。

How to perform Authorization in Java with Shiro

在Shiro中授权有四种:

  • 以编程方式 - 你可以在java代码中执行授权检查,其结构如if和else。
  • JDK注解 - 你可以将授权注解附加到Java方法。
  • JSP / GSP TagLibs - 你可以根据角色和权限来控制jsp或gsp页面输出

Programmatic Authorization

检查角色和权限,在你的代码中使用编程方式是一种传统方式来处理授权。

Role Check

这是一个使用编程方式来检查角色的栗子。我们想检查用户是否具有管理员角色,如果有,那么我们将显示一个特殊的按钮,否则我们不会显示它。

首先我们得到当前用户,也就是Subject。然后我们将admin传给Subject的hasRole()方法。它会返回TRUE或者 FALSE

1
2
3
4
5
6
7
8
//get the current Subject
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
//show a special button‏
} else {
//don’t show the button?)‏
}

现在,基于角色的检查是快速和容易实现的,但它具有主要的缺点。 这是隐式的。

如果你只想稍后添加,删除或重新定义角色怎么办?你必须打开源代码,并更改所有角色检查以反映你的安全模型的变化。你必须关闭应用程序,破解代码,测试它,然后重新启动它。

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

Permission Check

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

再次,首先你可以访问当前的用户,Subject。然后构造一个Permission对象或者实例来表示一个动作或者资源。在这个栗子中这个实例可以命名为printPermission,资源是laserjet3000n,动作是打印。然后我们将printPermission传给Subject的.isPermitted()方法。它返回true或者false。

1
2
3
4
5
6
7
8
9
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?
}

Permission Check (String-based)

你也可以用一个简单的字符串而不是permission类做权限检查。

因此,如果你不想实现我们permission 接口,你仅仅传一个string字符串就行了。在这个栗子中,我们传.isPermitted()一个字符串。

1
2
3
4
5
6
7
String perm = "printer:print:laserjet4400n";
if(currentUser.isPermitted(perm)){
//show the print button?
} else {
//don’t show the button?
}

只要你的域知道如何使用它,你可以按照你想要的方式构建权限字符串。在这个栗子中,我们使用Shiro的可选的权限语法, WildCardPermissions.WildCardPermissions功能强大直观。

通过使用基于字符串的权限检查,你可以获得的功能和之前的栗子是一样的。好处是你不必强制实现权限接口,你可以通过简单的字符串构造权限。缺点是你没有类型安全,如果你需要更复杂的许可权限,这些权限超出了这个处理的范围。接下来讲如何基于Permission接口实现权限对象。

Annotation Authorization

如果你不想在代码层做权限控制,你也可以使用Java注解。Shiro提供很多注解可以让你放在方法上面。

Enabling Annotation Support

在你使用Java注解之前,你需要在你的应用中开启AOP支持。有很多不同的AOP框架,因此不幸的是,在应用中没有一个标准的方式开启AOP。

对AspectJ而言,看栗子AspectJ sample application

对Spring而言,看栗子 Spring Integration

对Guice而言,看栗子 Guice Integration

Permission Check

在这个栗子中,我们想检查一个用户在调用openAccount方法之前是否有account:create权限。如果有,按照期望的调用,如果没得,那就抛异常。

和通过编程方式检查权限一样,你可以使用Permission对象或者简单的字符串方式。

1
2
3
4
5
6
7
//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
}

Role Check

在这个栗子中,我们想检查一个用户在执行openAccount方法前是否有teller角色。如果有就按照期望的走下去,没有就抛异常。

1
2
3
4
5
6
7
//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
}

JSP TagLib Authorization

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

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

首先,我们需要将Shiro taglib添加到我们的Web应用程序中。接着我们为user:manage权限添加标签。在标签中,如果用户具有我们正在检查的权限,我们就放置要执行的代码。如果我们要执行一个动作假如这个用户缺少权限,我们需要添加 标签,再次检查users:manage权限。如果用户缺少权限,我们想要执行的任何代码都需要放在标签中。

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ 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标签库,以及有关将应用程序集成到Web应用程序中的更多信息,请阅读Web集成文档.


留言: