shiro基于权限的授权

jopen 10年前

就像我们上面在角色概述中提到的,通过基于权限的授权执行访问控制是更好的方法。基于权限的授权,因为其与程序功能(以及程序核心资源上的行为)紧密联系,基于权限授权的源代码在程序功能改变时才需要改变,而与安全策略无关。这意味着与同样基于角色的授权相比,对代码的影响更少。

 

1.权限检查

如果你希望检查一个Subject是否允许做某件事情,你可以调用isPermitted*方法的变形,有两种主要方式检查授权--基于对象的权限实例或者代表权限的字符串

基于对象的权限检查

执行权限检查的一种方法是实例化一个Shiro的org.apache.shiro.authz.Permission接口并且将它传递给接收权限实例的*isPermitted方法。

例如,假设以下情景:办公室里有一台唯一标识为laserjet4400n的打印机,在我们向用户显示打印按钮之前,软件需要检查当前用户是否允许用这台打印机打印文档,检查权限的方式会是这样:

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?

}

在这个例子中,我们同样看到了一个非常强大的实例级别的访问控制检查--在单独数据实例上限制行为的能力。

基于对象的权限对下列情况非常有用:

希望编译期类型安全;

希望确保正确地引用和使用的权限;

希望对权限判断逻辑(称作权限隐含逻辑,基于权限接口的implies方法)执行进行明确控制;

希望确保权限正确地反映程序资源(例如,在一个对象域模型上创建一个对象时,权限类可能自动产生)。

下面是你可以根据需要调用的函数:

isPermitted(Permission p)

如果Subject允许执行特定权限实例综合的动作或资源访问返回真,否则返回假;

isPermitted(List perms)

按参数顺序返回isPermitted的结果数组,如果许多权限需要检查时非常有用(如定制一个复杂的视图);

isPermittedAll(Collection perms)

如果Subject拥有指定的所有权限返回真,否则返回假。

基于字符串的权限检查

虽然基于对象的权限检查很有用(编译期类型安全,对行为担保,定制隐含逻辑等),但在许多程序里有时候感觉有点笨重,另一种选择是用普通的字符串来代表权限实例

例如,对于上面打印权限的例子,我们可以使用字符串权限检查达到同样的结果:

Subject currentUser = SecurityUtils.getSubject();

if (currentUser.isPermitted("printer:print:laserjet4400n")) {

//show the Print button

} else {

//don't show the button?  Grey it out?

}

这个例子同样实现了实例级别的权限检查,但是所有主要权限部件--printer(资源类型)、print(动作)、laserjet4400n(实例ID)都表现为一个字符串。

上面的例子展示了一种以冒号分割的特殊形式的字符串,定义于Shiro的 org.apache.shiro.authz.permission.WildcardPermission实现中,它适合大多数用户的需求。

上面的代码块基本上是下面这段代码的缩写:

Permission p = new WildcardPermission("printer:print:laserjet4400n");

if (currentUser.isPermitted(p) {

//show the Print button

} else {

//don't show the button?  Grey it out?

}

WildcardPermission令牌形式和构成选项将在Shiro的Permission文档中深入讨论。

上面的字符串使用默认的WildcardPermission格式,实际上你可以创造并使用你自己的字符串格式,我们将在下面Realm授权章节讨论如何这样做。

基于字符串的权限有利的一面在于你不需要实现一个接口而且简单的字符串也非常易读,而不利的一面在于不保证类型安全,而且当你需要定义超出字符串表现能力之外的更复杂的行为时,你仍旧希望基于权限接口实现你自己的权限对象。实际上,大部分Shiro的终端用户回为其简单而选择基于字符串的方式,但最终你的程序需求决定了哪一种方法会更好。

和基于对象的权限检查方法一样,下面是字符串权限检查的函数:

isPermitted(String perm)

如果Subject被允许执行字符串表达的动作或资源访问权限,返回真,否则返回假;

isPermitted(String... perms)

按照参数顺序返回isPermitted的结果数组,当许多字符串权限需要检查时非常有用(如定制一个复杂的视图时);

isPermittedAll(String... perms)

当Subject具备所有字符串定义的权限时返回真,否则返回假。

 

2.权限判断

作为检查Subject是否被允许做某件事之后的一个选择,你可以在逻辑执行之前简单判断他们是否具备所需的权限,如果Subject不被允许,AuthorizationException异常被抛出,如果是允许的,判断将安静地执行并按期望顺序执行下面的逻辑。

例如:

Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted

//to open a bank account:

Permission p = new AccountPermission("open");

currentUser.checkPermission(p);

openBankAccount();

或者,同样的判断,可以用字符串形式:

Subject currentUser = SecurityUtils.getSubject();

//guarantee that the current user is permitted

//to open a bank account:

currentUser.checkPermission("account:open");

openBankAccount();

与isPermitted* 方法相比较,这种方法的优势是代码更为清晰,如果当前Subject不符合条件,你不必创建你自己的AuthorizationExceptions异常(如果你不想那么做)。

下面是你可以根据需要调用的函数:

checkPermission(Permission p)

如果Subject被允许执行特定权限实例指定的动作或资源访问,安静地返回,否则抛出AuthorizationException异常。

checkPermission(String perm)

如果Subject被允许执行权限字符串指定的动作或资源访问,安静地返回,否则抛出AuthorizationException异常。

checkPermissions(Collection perms)

如果Subject被允许执行所有权限实例指定的动作或资源访问,安静地返回,否则抛出AuthorizationException异常。

checkPermissions(String... perms) 和上面的checkPermissions效果一样,只是使用字符串权限类型。