理解Apache Shiro中的权限--官网

理解Apache Shiro中的权限

在Shiro中,权限代表了安全策略中最基本的元素,它们显式地表示在应用程序中可以做哪些事情,一个格式良好的权限语句,应该是描述了某个资源,以及当主体(Subject)与这些资源交互时可能发生的操作,例如以下权限语句

  • 打开一个文件
  • 浏览 ”/user/list" web 页面
  • 打印文档
  • 删除 "jsmith" 用户

大多数资源都应该会支持CRUD操作,任何对特定资源类型有意义的操作都是可以的,所以权限语句至少是基于资源和操作的,因此,在查看权限时,需要意识到权限没有表示谁可以执行所表示的行为,它们只是表示了在应用程序中可以做什么

权限仅仅代表行为,它们只是反映了对特定资源类型的操作,并不表示谁可以执行这些操作

允许谁(用户)做什么(权限)即为授权,这应该取决于你的应用程序的数据模型,不同的应用程序会有很大的不同

比如可以将多个权限集合起来可以组成一个角色,角色可以赋予给多个用户,这样被赋予角色的用户就拥有了该角色中的所有权限;也可以把多个用户组成一个用户组,给这个用户组赋予角色,该组中的所有用户都被隐式地授予了角色中的权限。对于如何将权限授予用户有许多不同的方式,应该根据需求确定如何对此进行建模

Wildcard Permissions

上面表述的权限的例子,如“打开文件”,“查看‘user/list'网页”,都是有效的权限字符串,但是,以自然语言的方式来表示的权限是难以理解,所以Shiro提供了强大且直观的权限语句的语法,即WildcardPermission,来保证权限语句可读并且易于使用

简单使用

假设以下场景,在公司中有许多打印机(printer),一些人可以在部分打印机上进行打印,其他人可以查看当前打印任务队列中还有哪些

一个简单的方式即赋予用户一个“queryPrinter”权限,这样就可以调用下面的代码来检查用户是否有queryPrinter权限:

subject.isPermitted("queryPrinter");

这和以下的代码是等效的

subject.isPermitted(new WildcardPermission("queryPrinter"));

当然,这个例子需要你的应用程序中有诸如“printPrinter”,“queryPrinter”,“managePrinter”等权限,也可以通过通配符“*”来表示拥有整个应用程序中的所有权限,但是使用这种方法无法让一个用户拥有所有的打印机权限。因为WildcardPermission支持多级权限

多级权限字符串

WildcardPermission支持多级权限的概念,上面的例子可以构造成如下的权限字符串

printer:query

冒号作为权限字符串各个部分的分隔符,上面的例子中,第一部分表示资源类型(printer),第二部分表示操作(query),其他的例子如下:

printer:print
printer:manager

一个权限字符串没有具体要求应该分为几部分,这取决于在应用程序中使用的方式

一个部分有多个值

权限字符串分隔的每一部分可以包含多个值,每个值之间用逗号分隔,比如需要给用户同时赋予“printer:print”和“printer:query”权限,可以简化为:

printer:print,query

如果用户已经有了上面的权限,则下面的代码将返回true

subject.isPermitted("printer:query")

一个部分的所有值

如果想把某一部分的所有值都赋予给用户,则可以采用通配符“*”的形式,比如上面的打印机资源总共有三种操作(query,print和manage),则可以将所有值都列出来然后把权限授予用户

printer:query,print,manage

也可以简写为:

printer:*

这样,任何形如"printer:XXX"的权限检查都会返回true。通配符可以出现在权限字符串的任一部分,用以表示该部分的所有值

实例级访问控制

Shiro支持实例级的访问控制,也是以冒号分隔,其中第一部分为资源类型,第二部分为操作,第三部分为实例,如下

printer:query:lp7200
printer:print:epsoncolor

第一个定义了ID为lp7200printer类型的查询权限,第二个定义了ID为epsoncolorprinter类型的打印权限。这样就可以实现实例级别的授权,如下代码演示了这种授权的权限检查方式

if (SecurityUtils.getSubject().isPermitted("printer:query:lp7200")){
    // Return the current jobs on printer lp7200
}

这种授权虽然强大,但是不具备扩展性,如果增加了新的打印机,则需要定义新的权限,这时可以结合通配符来解决,如下:

printer:print:*
printer:*:*
printer:*:lp7200
printer:query,print:lp7200

默认值

如果权限字符串的某一部分没有,则其默认为通配符“*”,如

# 这两个权限字符串是等效的
printer:print
printer:print:*

# 这两个权限字符串是等效的
printer
printer:*:*

# 但是缺省值只能是权限字符串的末端开始,如下面两个权限就不是等效的
printer:lp7200
printer:*:lp7200

权限检查

为了遍历和扩展性,权限可以采用通配符的方式,但是在进行权限检查时,我们应该总是基于最小权限进行检查

举个例子,如果一个用户想要使用id为lp7200的打印机打印文档,我们应该采用如下代码进行检查:

if ( SecurityUtils.getSubject().isPermitted("printer:print:lp7200") ) {
    //print the document to the lp7200 printer }
}

这个代码准确的反映了用户想要做什么。但是下面的代码就缺点意思了:

if ( SecurityUtils.getSubject().isPermitted("printer:print") ) {
    //print the document }
}

考虑一种特殊情况,用户拥有printer:print:lp7200权限,但是用户没有所有打印机的权限,因此这个代码会阻止用户打印,这是个不正确的权限检查。因此,在写权限检查的代码时,应该进行最小权限的检查

Implication, not Equality

在进行权限检查时,应该尽可能的具体,但是在给用户授权时,应该让权限尽可能的通用一些。权限检查是通过其隐含意义而不是等值比较来进行计算的。比如给用户授权时是user:*,那这隐含了用户拥有user:view的权限,字符串user:*和字符串user:view并不是相等的,但是前者包含了后者,user:*user:view的超集

为了支持这个规则,所有的权限都会转换成org.apache.shiro.authz.Permission接口的实例,这样就可以在运行时执行隐含权限的检查,而且隐含权限的检查比简单的字符串相等检查更复杂。本文档中描述的所有通配符行为实际上都可以通过org.apache.shiro.authz.permission.WildcardPermission来实现,这里是一些例子:

# 第一个权限字符串隐含了第二个权限
user:*
user:delete

# 第一个权限字符串隐含了第二个权限
user:*:12345
user:update:12345

# 第一个权限字符串隐含了第二个权限
printer
printer:print

性能

因为权限检查是基于其隐含意义而不是等值比较的,因此每次权限检查时都会去查看赋予给用户的权限是否隐含了我们需要的权限,这是一个复杂的操作,为了提升性能,Shiro会将第一次检查成功的结果缓存起来,以后再次进行相同的检查时会立即返回

Shiro中有一个CacheManager组件,其负责缓存的相关工作,当CacheManager将用户、角色和权限缓存在内存中时,这会让权限检查非常快。但是,如果分配给用户或其角色或组的权限数量增加时,执行权限检查的时间也必然会增加

如果应用程序的Realm可以用更加有效的方法来执行权限检查,那就应该去实现RealmisPermitted*方法,虽然默认的Realm/WildcardPermission能够满足大部分的需求,但是对于一些有大量权限检查需求的应用来说,它们或许不是最好的解决方案

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容