Shiro—授权
一.概述
授权是指管理资源的访问,即控制谁可以访问什么。授权包括:是否允许用户查看此页面,编辑此数据,查看此按钮或打印到此打印机等。这些都决定了用户可以访问的内容。在Shiro中主要通过权限,角色和用户进行授权管理。
1. 权限
Shiro中的权限代表安全策略中的原子元素。它们基本上是关于行为的陈述,并明确表示可以在应用程序中完成的任务。格式良好的权限声明实质上描述了资源以及Subject与这些资源交互时可能采取的操作,如典型的CRUD操作,任何对特定类型资源的操作都可以作为权限,权限声明至少基于资源和操作。 定义谁(用户)被允许做什么(权限)以某种方式分配权限给用户,这始终由应用程序的数据模型完成,并且可以在不同应用程序之间有很大差异。例如,权限可以在角色中分组,并且该角色可以与一个或多个用户对象相关联。或者某些应用程序可以拥有一组用户,并且可以为一个组分配一个角色,通过传递关联将意味着该组中的所有用户都被隐式授予角色中的权限。如何为用户授予权限有很多变化,应用程序根据应用程序要求确定如何对其进行建模。
2. 角色
角色是一个命名实体,通常代表一组行为或责任。这些行为转化为可以或不可以使用软件应用程序执行的操作。角色通常分配给用户帐户,因此通过关联,用户可以“执行”归因于各种角色的事物。Shiro支持这两种概念:
-
隐式角色:大多数人将角色用作隐式构造,应用程序仅基于角色名称隐含一组行为(即权限)。对于隐式角色,软件级别没有任何内容表示“允许角色X执行行为A,B和C”,行为仅由名称暗示。隐式角色可能会给软件带来许多维护和管理问题。例如,如果您只想添加或删除角色,或稍后重新定义角色的行为,该怎么办?每次需要进行此类更改时,都必须返回源代码并更改所有角色检查以反映安全模型的变化!更不用说这会产生的运营成本(重新测试,通过QA,关闭应用程序,使用新的角色检查升级软件,重新启动应用程序等)。这对于非常简单的应用程序来说可能是好的(例如,可能存在’管理员’角色和’其他人’)。但对于更复杂或可配置的应用程序,这可能是应用程序整个生命周期中的主要问题,并为您的软件带来巨大的维护成本。
-
显式角色:显式角色本质上是实际权限的命名集合。在这种形式下,应用程序(和Shiro)确切地知道具有特定角色的含义。因为已知可以执行与否的确切行为,所以没有猜测或暗示特定角色可以做什么或不能做什么。该方式可以更好地控制应用程序的安全性。
3. 用户
用户本质上是应用程序的“谁”。正如我们之前所述,这Subject确实是Shiro的“用户”概念。允许用户(主题)通过与角色或直接权限的关联在应用程序中执行某些操作。应用程序的数据模型确切地定义了Subject允许执行某些操作的方式。例如,数据模型中,可能拥有一个实际的User类,并且可以直接为User实例分配权限。或者,可能只是Roles直接分配权限,然后将角色分配给Users,因此通过关联,可以用Users传递“拥有”分配给其角色的权限。或者你可以用“群体”概念来表达这些东西。数据模型确切地定义了授权的功能。Shiro依靠Realm实现将您的数据模型关联细节转换为Shiro理解的格式。
一.授权方式
1. 代码授权
- 基于角色授权
如果只想检查当前Subject是否拥有某角色的权限,可以调用在Subject的hasRole方法:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
//show the admin button
} else {
//don't show the button? Grey it out?
}
- 基于权限授权
如果要检查当前Subject是否允许某些操作,可以调用sPermitted方法:
Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted(printPermission)) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
- 基于字符串授权
根据上面的打印权限示例,我们可以将相同的检查重新制定为基于字符串的权限:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("printer:print:laserjet4400n")) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
2. 注释授权
- @RequiresAuthentication
@RequiresAuthentication
public void updateAccount(Account userAccount) {
//this method will only be invoked by a
//Subject that is guaranteed authenticated
...
}
大致等同于以下代码:
public void updateAccount(Account userAccount) {
if (!SecurityUtils.getSubject().isAuthenticated()) {
throw new AuthorizationException(...);
}
//Subject is guaranteed authenticated here
...
}
- @RequiresGuest
@RequiresGuest
public void signUp(User newUser) {
//this method will only be invoked by a
//Subject that is unknown/anonymous
...
}
大致等同于以下代码:
public void signUp(User newUser) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals != null && !principals.isEmpty()) {
//known identity - not a guest:
throw new AuthorizationException(...);
}
//Subject is guaranteed to be a 'guest' here
...
}