上一篇博文总结了一下Shiro中的身份认证,本文主要来总结一下Shiro中的权限认证(Authorization)功能,即授权。如下:
本文参考自Apache Shiro的官方文档:http://shiro.apache.org/authorization.html。
本文遵循以下流程:先介绍Shiro中的权限认证,再通过一个简单的实例来具体说明一下API的使用(基于maven)。
1. 权限认证的核心要素
权限认证,也就是访问控制,即在应用中控制谁能访问哪些资源。在权限认证中,最核心的三个要素是:权限,角色和用户:
权限(permission):即操作资源的权利,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的权利;
角色(role):指的是用户担任的的角色,一个角色可以有多个权限;
用户(user):在Shiro 中,代表访问系统的用户,即上一篇博文提到的Subject认证主体。
它们之间的的关系可以用下图来表示:
一个用户可以有多个角色,而不同的角色可以有不同的权限,也可由有相同的权限。比如说现在有三个角色,1是普通角色,2也是普通角色,3是管理员,角色1只能查看信息,角色2只能添加信息,管理员都可以,而且还可以删除信息,类似于这样。
2.1 基于角色的访问控制
也就是说,授权过程是通过判断角色来完成的,哪个角色可以做这件事,哪些角色可以做这件事等等。它有如下API:
方法 | 作用 |
---|---|
hasRole(String roleName) | 判断是否有该角色访问权,返回boolen |
hasRoles(List<String> roleNames) | 判断是否有这些这些角色访问权,返回boolean[] |
hasAllRoles(Collection<String> roleNames) | 判断是否有这些这些角色访问权,返回boolean |
对这三个API,做一下简单的说明,第一个很简单,传入一个role即可,判断是否拥有该角色访问权,第二个方法是传入一个role的集合,然后Shiro会根据集合中的每一个role做一下判断,并且将每次的判断结果放到boolean[]数组中,顺序与集合中role的顺序一致;第三个方法也是传入一个role的集合,不同的是,返回boolean类型,必须集合中全部role都有才为true,否则为false。
用法如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Subject currentUser = SecurityUtils.getSubject(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (currentUser.hasRole(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"administrator"</span>)) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//show the admin button or do administrator's things</span> } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//don't show the button? Grey it out? or others...</span> }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
除了这三个API外,Shiro还提供了check的API,与上面不同的是,has-xxx会返回boolean类型的数据,用来判断,而check-xxx不会返回任何东西,如果验证成功就继续处理下面的代码,否则会抛出一个异常,可以用来通过捕获异常来处理。API如下:
方法 | 作用 |
---|---|
checkRole(String roleName) | 如果判断失败抛出AuthorizationException异常 |
checkRoles(String... roleNames) | 如果判断失败抛出AuthorizationException异常 |
checkRoles(Collection<String> roleNames) | 如果判断失败抛出AuthorizationException异常 |
类似的使用方法如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Subject currentUser = SecurityUtils.getSubject(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//guarantee that the current user is a bank teller and</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//therefore allowed to open the account:</span> currentUser.checkRole(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"bankTeller"</span>); openBankAccount();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
2.2 基于权限的访问控制
基于权限的访问控制和基于角色的访问控制在原理上是一模一样的,只不过API不同而已,我不再做过多的解释,API如下:
方法 | 作用 |
---|---|
isPermitted(String perm) | 判断是否有该权限,返回boolen |
isPermitted(List<String> perms) | 判断是否有这些这些权限,返回boolean[] |
isPermittedAll(Collection<String> perms) | 判断是否有这些这些权限,返回boolean |
checkPermission(String perm) | 如果判断失败抛出AuthorizationException异常 |
checkPermissions(String... perms) | 如果判断失败抛出AuthorizationException异常 |
checkPermissionsAll(Collection<String> perms) | 如果判断失败抛出AuthorizationException异常 |
3. 权限认证示例代码
不管是身份认证还是权限认证,首先都需要创建SecurityManager工厂,SecurityManager,所以首先新建一个工具类专门做这个事情。
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ShiroUtil</span> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Subject <span class="hljs-title" style="box-sizing: border-box;">login</span>(String configFile, String username, String password) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 读取配置文件,初始化SecurityManager工厂</span> Factory<SecurityManager> factory = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IniSecurityManagerFactory(configFile); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 获取securityManager实例</span> SecurityManager securityManager = factory.getInstance(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 把securityManager实例绑定到SecurityUtils</span> SecurityUtils.setSecurityManager(securityManager); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 得到当前执行的用户</span> Subject currentUser = SecurityUtils.getSubject(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 创建token令牌,用户名/密码</span> UsernamePasswordToken token = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> UsernamePasswordToken(username, password); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span>{ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 身份认证</span> currentUser.login(token); System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"身份认证成功!"</span>); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span>(AuthenticationException e){ e.printStackTrace(); System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"身份认证失败!"</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> currentUser; } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>
提供一个静态方法,返回当前用户,在应用程序中我们直接调用这个类中的静态方法即可返回当前认证的用户了。
maven中的pom.xml文件内容和上一节一样的,不再赘述。
Shiro的配置文件shiro.ini:
<code class="language-ini hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">#用户,role表示各个角色 <span class="hljs-title" style="box-sizing: border-box;">[users]</span> <span class="hljs-setting" style="box-sizing: border-box;">csdn1=<span class="hljs-value" style="box-sizing: border-box;"><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">123</span>,role1,role2,role3</span></span> <span class="hljs-setting" style="box-sizing: border-box;">csdn2=<span class="hljs-value" style="box-sizing: border-box;"><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">123</span>,role1,role2</span></span> #定义不同角色都拥有哪些权限 <span class="hljs-title" style="box-sizing: border-box;">[roles]</span> <span class="hljs-setting" style="box-sizing: border-box;">role1=<span class="hljs-value" style="box-sizing: border-box;">user:select</span></span> <span class="hljs-setting" style="box-sizing: border-box;">role2=<span class="hljs-value" style="box-sizing: border-box;">user:add,user:update</span></span> <span class="hljs-setting" style="box-sizing: border-box;">role3=<span class="hljs-value" style="box-sizing: border-box;">user.delete</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
角色认证:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RoleTest</span> {</span> <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Test</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">testHasRole</span>() { String configFile = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"classpath:shiro.ini"</span>; String username = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"csdn2"</span>; String password = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"123"</span>; Subject currentUser = ShiroUtil.login(configFile, username, password); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//测试hasRole</span> System.out.println(currentUser.hasRole(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role2"</span>)? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"有role2这个角色"</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"没有role2这个角色"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//测试hasRoles</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span>[] results = currentUser.hasRoles(Arrays.asList(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role1"</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role2"</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role3"</span>)); System.out.println(results[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"有role1这个角色"</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"没有role1这个角色"</span>); System.out.println(results[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"有role2这个角色"</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"没有role2这个角色"</span>); System.out.println(results[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>]? <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"有role3这个角色"</span> : <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"没有role3这个角色"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//测试hasAllRoles System.out.println(currentUser.hasAllRoles(Arrays.asList("role1","role2","role3")));</span> currentUser.logout(); } <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Test</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">testCheckRole</span>() { String configFile = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"classpath:shiro.ini"</span>; String username = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"csdn2"</span>; String password = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"123"</span>; Subject currentUser = ShiroUtil.login(configFile, username, password); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// currentUser.checkRole("role3");//没有返回值。有就不报错,没有就会报错</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// currentUser.checkRoles(Arrays.asList("role1","role2","role3")); //同上</span> currentUser.checkRoles(Arrays.asList(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role1"</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"role2"</span>)); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//同上</span> currentUser.logout(); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li></ul>
权限认证和角色认证的测试一样的,我就不再赘述了。当然了,这里只是单纯的测试,实际中,认证完了后还要做一些具体的业务逻辑处理。
—–乐于分享,共同进步!
—–我的博客主页:http://blog.csdn.net/eson_15