Shiro的基于字符串通配符的权限表达式

深入理解Apache Shiro的Permissions

自己的理解:

首先我们要知道我们使用realm的doGetAuthorizationInfo()在返回的SimpleAuthorizationInfo中要设置两部分角色和权限。

@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));

        return authorizationInfo;
    }

一部分是角色role,一个部分是权限permission。这样我们在对资源进行控制时可以单独使用角色控制,也可以单独使用权限控制,或者角色+权限控制例如:
单独权限控制:

@RequiresPermissions("user:create")
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String create(User user, RedirectAttributes redirectAttributes) {
    userService.createUser(user);
    redirectAttributes.addFlashAttribute("msg", "鏂板鎴愬姛");
    return "redirect:/user";
}

单独角色控制:



     User Page

Shiro 默认使用 WildcardPermission 这个类进行权限匹配。而这个类对权限的表达式就有了限制就是我们本文章下面介绍的*【深入理解Apache Shiro的Permissions】*,即这个类对权限表达式的限制,或者说使用这个类就必须按照下面的规则来设置你的权限字符串。

自己的总结:

WildcardPermission规定:权限可以又三部分组成,每一部分用冒号分割
三个部分——第一个是域,第二个是动作,第三个是被执行的实例(标识)
user:query:2
我们应该可以理解三部分分别为:user为user类或表或user对应的菜单,query为查询权限,id为2的user。
实际开发中我们可以用前面两部分如user:*代表所有权限或者只用中间那一部分就可以如query只是单权限,user:create我们可以理解为对user表的create权限,那么我们可以使用注解把这个权限加到userController中的add方法上面或者加到jsp添加按钮的标签上或者访问user的添加url拦截器中。那么再访问这个方法或者添加按钮标签显示与否或者user的添加url能否访问成功,就看用户有无这个权限。

@RequiresPermissions("user:create")
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String create(User user, RedirectAttributes redirectAttributes) {
    userService.createUser(user);
    redirectAttributes.addFlashAttribute("msg", "成功");
    return "redirect:/user";
}

当然我们也可以定义自己的权限匹配类来使用,请看另一篇博客:
https://blog.csdn.net/lw_power/article/details/52562749
https://blog.csdn.net/lw_power/article/details/52562754

通配符的权限

单个权限: 直接起一个字符串名即可

例如: queryPrinter权限-查询权限

subject.isPermitted("queryPrinter")
   
   
   
   
  • 1

基本等同于:

subject.isPermitted( new WildcardPermission("queryPrinter") )
   
   
   
   
  • 1

第二种方式基本不用,用第一种方式即可

多个权限: 通配符权限支持多个级别或部分的概念。

下面使用”:”用于分隔权限字符串下一部分的特殊字符。

printer:query
printer:print
printer:manage
   
   
   
   
  • 1
  • 2
  • 3

即可配置多个权限

也可以用多值来配置:

printer:print,query
   
   
   
   
  • 1

验证查询权限:

subject.isPermitted("printer:query")
   
   
   
   
  • 1

单个资源的所有权限

比如我们有这些权限:

printer:query,print,manage
   
   
   
   
  • 1

相当于:

printer:*
   
   
   
   
  • 1

使用第二种方法使用通配符比显式地列出动作要更好,因为如果以后向应用程序添加了一个新操作,则不需要更新在该部分中使用通配符的权限。

所有资源的某个权限

还可以在通配符权限字符串的任何部分使用通配符令牌

*:view
   
   
   
   
  • 1

所有资源的view权限
也就是说对“foo:view”(或其他的:view)的任何权限检查将返回true

实例级别的权限控制

通配符权限的另一个常见用法是建立实例级访问控制列表。
在这个权限中,您将使用三个部分——第一个是域,第二个是动作,第三个是被执行的实例(标识)。

单个实例的单个权限

printer:query:lp7200
printer:print:epsoncolor
   
   
   
   
  • 1
  • 2

比如你拥有printer的query权限,打印机的id为lp7200,也就是拥有这类printer的query权限

如果您将这些权限授予用户,那么它们就可以在特定的实例上执行特定的行为。然后你可以在代码中做一个检查:

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

所有实例的单个权限

printer:print:*
   
   
   
   
  • 1

也就是说,具有所有printer的print权限,相当于前面的单个资源的多个权限

所有实例的所有权限

printer:*:*
   
   
   
   
  • 1

单个实例的所有权限

printer:*:lp7200
   
   
   
   
  • 1

单个实例的多个权限

printer:query,print:lp7200
   
   
   
   
  • 1

query和print之间用逗号隔开
在实际开发中,基本上用不到实例级别的权限控制

关于权限分配的最后一件事是:末尾丢失的部分意味着用户可以访问与该部分对应的所有值。换句话说,

printer:print
就相当于:
printer:print:*
   
   
   
   
  • 1
  • 2
  • 3
printer 
单个权限相当于
printer:*:*
   
   
   
   
  • 1
  • 2
  • 3

但是注意!

printer:lp7200printer:*:lp7200
是不同的!!!
   
   
   
   
  • 1
  • 2
  • 3
  • 4

因为这不是末尾的*

检查权限

虽然权限分配使用通配符构造相当多(“printer:*”=打印到任何printer),但在运行时的权限检查应该始终基于可能的最特定的权限字符串。
比如:如果用户有一个用户界面,他们想要打印一个文档到lp7200打印机,你应该检查用户是否允许执行这个代码

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

这个检查非常具体,并且明确地反映了用户在那个时候正在尝试做什么。
但是,如下代码是不对的:

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

因为第二个示例说“您必须能够打印到任何打印机,以便执行以下代码块”。但请记住,“printer:print”等同于“printer:print:*”!

因此,这是一个不正确的检查。
如果当前用户没有能力打印到任何打印机,但他们确实有打印的能力,比如lp7200和epsoncolor打印机。
然而,上面的第二个例子永远不会允许他们打印到lp7200打印机,即使他们已经获得了这种能力!

因此,经验法则是在执行权限检查时使用最特殊的权限字符串。
当然,如果您真的只想执行代码块,如果用户被允许打印到任何打印机(可能),那么第二个方法可能是应用程序中的另一个有效的检查。
您的应用程序将决定什么检查是有意义的,但是一般来说,越具体越好。

为什么运行时权限检查应该尽可能具体,但是权限分配可以更通用一些呢?
这是因为权限检查是由隐含逻辑计算的,而不是平等检查。

也就是说,如果用户被分配给”user:“权限,这意味着用户可以执行”user:view”操作。字符串”user:“显然不等于”user:view”,但前者暗示后者。”user:*”描述了由”user:view”定义的功能的超集。

为了支持隐含规则,所有权限都被翻译到实现org.apache.shiro.authz的对象实例的权限接口中。
这就是说,隐含逻辑可以在运行时执行,而且隐含逻辑通常比简单的字符串等式检查更复杂。
本文档中描述的所有通配符行为实际上都是由org.apache.shiro.authz.permission.WildcardPermission类实现

下面是一些通配符的权限字符串,它显示了访问的含义:

user:*
   
   
   
   
  • 1

暗指还能删除用户的能力:

user:delete
   
   
   
   
  • 1

但是:

user:*:12345
   
   
   
   
  • 1

也就是说,还可以使用实例12345更新用户帐户:

user:update:12345
   
   
   
   
  • 1
printer
暗示了打印机的任何功能,比如:
printer:print
   
   
   
   
  • 1
  • 2
  • 3

授权流程

授权其实就是查看有没有权限,有就授权给它

授权步骤:

Step 1: Application or framework code invokes any of the Subject hasRole*, checkRole*, isPermitted*, or checkPermission* method variants, passing in whatever permission or role representation is required.

Step 2: The Subject instance, typically a DelegatingSubject (or a subclass) delegates to the application’s SecurityManager by calling the securityManager’s nearly identical respective hasRole*, checkRole*, isPermitted*, or checkPermission* method variants (the securityManager implements the org.apache.shiro.authz.Authorizer interface, which defines all Subject-specific authorization methods).

Step 3: The SecurityManager, being a basic ‘umbrella’ component, relays/delegates to its internal org.apache.shiro.authz.Authorizer instance by calling the authorizer’s respective hasRole*, checkRole*, isPermitted*, or checkPermission* method. The authorizer instance is by default a ModularRealmAuthorizer instance, which supports coordinating one or more Realm instances during any authorization operation.

Step 4: Each configured Realm is checked to see if it implements the same Authorizer interface. If so, the Realm’s own respective hasRole*, checkRole*, isPermitted*, or checkPermission* method is called.
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

有兴趣的可以去官网看看:http://shiro.apache.org/authorization.html

你可能感兴趣的:(Shiro)