2013年2月2日土曜日

Apache Shiro Java 授权指南


译者:刘晓日
授权,也叫访问控制,提供特定资源访问权限的功能。或者说是什么样的用户可以访问哪些特定资源。
授权检查的例子有:用户是否可以访问某网页,是否可以编辑数据,按钮对其是否可见,或者是否允许打印等。这些都是由哪些用户可以访问哪些资源决定的。

授权组成部分

授权有三个核心要素:权限、角色、用户。
权限定义
权限是安全领域内的最小单元,且具有陈述功能性。权限代表用户可以使用应用程序完成哪些工作。一个定义良好的权限不仅可以表述资源的类型,还可展示出在与资源交互时可进行哪些操作。比如:你有打开门的权限吗?你对这个文件有读的权限吗?你能删除客户记录吗?这个按钮你能点击吗?
数据相关的操作有增、删、改、查,通常简称为CRUD。
权限的作用不是指明哪个用户可以进行哪些操作,而是仅仅描述可进行的那些操作。
权限级别
上面提到的权限是针对某类资源(门、文件、客户记录等等)上的操作(打开、读、删等等)。在Shrio中,可以定义任何级别的权限,下面是几个常用的权限级别:
  • 资源级别:应用最广泛,最易使用的权限。一个用户可以编辑用户信息或打开一扇门,这里指定的是一类资源,而不是某资源特定的实例。
  • 实例级别:这类权限是针对某类资源的实例。比如:某用户可以编辑IBM公司的客户记录,或者可以打开厨房的门。
  • 属性级别:这类权限针对某类资源或实例的属性。比如:某用户可以编辑IBM公司客户的地址信息。
预了解权限更详细信息,请查阅权限文档
角色定义
在授权范围内,角色实际上就是权限的集合,用来简化用户和权限管理。这样,用户就可以分配给角色,而不是直接与权限做关联。进而解决复杂应用和较大用户数量带来的复杂性。比如银行应用系统中可能有管理员和柜员两种角色。
让我们了解下Shiro内置的两种角色。
隐含角色
很多人把角色看做是隐含在应用中的一组权限的集合,这是因为用户拥有一个特定的角色,而这个特定的角色又与一组权限相关联或者应用中为这些权限做检查。一般代码中的角色检查就反应了一种隐含的角色。拥有管理员角色,就可以查看病人信息,拥有柜员角色就可以创建银行账户。实际上,这些名字和软件能完成的工作没有相关性,很多人就是这样去使用角色,当然这样是最简单的,但是它会给简单的应用程序带来很多的维护和管理上的问题。
显式角色
显式角色有明确的权限与之关联,所以显式角色是一个权限的集合。代码中的权限检查就反应了一种显式角色。你可以查看病人信息是因为管理员角色拥有查看病人信息的权限,你可以创建银行账户因为柜员角色拥有创建银行账户的权限。但是完成这些操作不是因为基于名称的隐含角色,而是因为这种角色拥有该操作的权限。
显式角色给应用带来的最大好处是易于管理和低维护成本。增加、修改、删除角色时,根本不需要更改源代码。当然,在Shiro中也可以在运行时动态添加、删除、修改角色,而且授权检查时会追加当前日期,这样就不需要强制用户登出再登入才能感知到新权限的存在。
用户定义
用户是应用中的“who”,Shiro中用户的概念是Subject实例。之所以使用Subject替代用户概念,是因为用户通常都是指人,而Shiro中Subject指的是任何与系统交互的“东西”,不管是人或是服务。
用户通过与之关联的角色或直接关联的权限可以执行应用中的某些操作。用户可以查看客户数据,是因为用户拥有查看客户数据的权限,或者与之关联的角色拥有查看客户数据的权限。
预了解用户(也叫Subject)更多信息,请参阅Subject Document
最后,应用中自己实现的realm是用来与数据源(RDBMS,LDAP等等)交互的,它将告知Shiro是否存在这样的角色或者权限。对授权模型如何工作,全权由我们自己控制。

如何使用Shiro在java中进行授权

Shiro中的授权有三种实现方式:
  • 编程实现:在Java代码中使用if,else结构实现授权检查。
  • JDK注解:在Java方法上附加授权注解实现。
  • JSP/GSP标签库:基于角色和权限控制jsp或gsp页面的输出。
编程实现授权
通过编写java代码检查权限和角色是一种很传统的处理授权方式。下面就是在Shiro中如何通过编码方式检查权限与角色。
角色检查
这个例子是在应用中通过编码实现角色检查功能。检查用户是否用管理员角色,如果有则显示一个按钮,如果没有就不显示。
首先要得到当前用户,也就是Subject。然后将“administrator”作为参数传递给Subject的hasRole()方法,这个方法的返回值是true或false。
//获取当前用户
Subject currentUser =SecurityUtils.getSubject();
if (currentUser.hasRole(“administrator”)) {
    //显示按钮
} else {
    //隐藏按钮
}
这样一个简单快捷的基于角色检查的实现就完成了,但是它有一个很大的缺点,这个缺点还很隐晦。
想想随后增加、删除,或者修改角色会发生什么?当然是必须修改源代码以及所有与角色检查相关的地方,来应对安全模型上的改变。而且每次都要停掉应用的服务,修改源代码,测试通过,最终启动服务。
这种方式适用于规模较小应用,但是对于规模较大的项目来说,这种方式只会成为项目生命周期中的主要问题,而且还会增加项目维护成本。
权限检查
下面是一个通过权限实现安全检查的示例。示例中检查用户是否有打印“laserjet3000n”的权限,如果有则显示打印按钮,没有就不显示。这是一个实例级别的权限示例,或者叫实例级别的授权示例。
首先还是要获取当前用户,也就是Subject,然后构造一个权限(Permission)实例或者代表某资源上操作的实例。示例中实例是printerPermission,laserjet3000n是资源,资源上的操作是print。接下来将printerPermission作为参数传递给Subject的isPermitted()方法,它的返回值为true或false。
Subject currentUser = SecurityUtils.getSubject();
Permission printPermission = new PrinterPermission(“laserjet3000n”,“print”);
If (currentUser.isPermitted(printPermission)) {
    //do one thing (show the print button?)‏
} else {
    //don’t show the button?
}
基于字符串的权限检查
也可以使用一个简单的字符串替代permission类来实现权限检查。
如果不想实现权限接口,那么只需要用一个字符串作为参数。示例中将字符串printer:print:LaserJet4400n 作为参数传递给isPermitted()。
String perm = printer:print:laserjet4400n”;
if(currentUser.isPermitted(perm)){
    //show the print button?
} else {
    //don’t show the button?
}
只要自定义的Realm能够解析权限字符串,可以用任意形式来定义。示例中使用Shiro可选的WildCardPermissions表达式。WildCardPermissions是一种功能强大,而且很直观的表达式。
使用基于字符串的权限检查可以实现与前面示例一样的效果,这种方式的优点在于不在需要实现权限接口,使用一个简单的字符串就可以构造一种权限。它的不足之处是类型不安全。当想构造它能表达范围之外的权限时,也只能去实现权限接口创建自己的权限对象。
授权注解
如果不愿意使用编码的方式实现授权,那么可以使用Java的注解来实现授权。Shiro提供了很多Java 注解enter link description here用来为方法添加注解。
开启注解
使用Java注解前,需要先在应用中开启对AOP的支持。不幸的是,很多AOP框架的存在,导致了没有一个标准的在应用中开启对AOP支持的方法。
对于AspectJ框架,参阅AspectJ 简单应用
对于Spring框架,参阅Spring 集成文档
对于Guice框架,参阅Guice集成文档
权限检查
这个示例中,在调用openAccount方法前,检查用户是否有account:create权限。如果有,方法则如期执行,否则抛出异常。
与编码方式做权限检查一样,使用注解时既可以使用权限对象,也可以使用简单字符串方法。
//如果调用者的角色中不具有账户创建的权限,则会抛出AuthorizationException异常
@RequiresPermissions(“account:create”)‏
public void openAccount( Account acct ) { 
    //create the account
}
角色检查
这个示例中,调用openAccount方法前,检查用户是否有teller角色。如果有,方法则如期执行,否则抛出异常。
//如果调用者不具有teller角色,则抛出AuthorizationException异常。
@RequiresRoles( teller )
public void openAccount( Account acct ) { 
    //do something in here that only a teller
    //should do
}
授权JSP标签库
对于JSP/GSP的web应用,Shiro提供了标签库来实现授权。
示例中,通过user:manage权限来控制前往用户管理页面的链接。如果用户没有user:manage权限,则会给出友好的信息提示。
首先需要将Shiro标签库加载到应用中,然后使用标签检查是否拥有user:manage权限。检查通过则会执行 标签内部的代码。还可以使用标签来检查用户不具有user:manage权限,不具有权限时想执行的代码放到标签中即可。
<%@ taglib prefix=“shiro uri=http://shiro.apache.org/tags %>
<html>
<body>
    <shiro:hasPermission name=“users:manage”>
        <a href=“manageUsers.jsp”>
            Click here to manage users
        </a>
    </shiro:hasPermission>
    <shiro:lacksPermission name=“users:manage”>
        No user management for you!
    </shiro:lacksPermission>
</body>
</html>
当然,还有很多标签用于检查角色和用户数据与状态。
想更多的了解JSP/GSP标签的信息,请参阅JSP标签库。想更多了解web集成方面信息的,请参阅Web集成文档

0 件のコメント:

コメントを投稿