视图层安全
大多数Web应用中总有一些页面元素我们只希望向某些特定用户展示,而Spring提供的filter只能限定哪些用户访问页面,即它只能提供页面层次的粗粒度过滤功能。如果要实现细粒度的访问控制,即控制哪些用户可以访问页面上的特定元素,你需要使用Spring提供的JSP标签库。这个标签库只有3个标签:<authz:acl>,<authz:authentication>和<authz:authorize>。若要在JSP页面中使用这些标签,必须使用JSP的<%@taglib%>命令来导入标签库
<%@ taglib prefix=”authz” uri="http://acegisecurity.org/authz" %>
下面让我们来看看如何使用这些标签库,先从<authz:authorize>开始。
1. 有条件地提供内容
< authz:authorize >是Spring安全机制中最常用的JSP页面标签。它能高效地执行一个if语句,判断当前用户是否有适当权限来查看页面特定内容。如果有将会提供标签主体。否则,标签内容将会被忽略。
举例来说,我们要在RoadRantz应用上添加一条欢迎信息和一个注销的连接。对于未验证用户这没有意义。因此,我们想要在这之前确定用户是否有权限。我们可以使用<authz:authorize>的ifAllGranted属性向视图添加内容,例如:
<authz:authorize ifAllGranted=”ROLE_MOTORIST, ROLE_VIP”>
Welcome Motorist!<br/>
<a href=”j_acegi_logout”>Logoff</a>
</authz:authorize>
因为使用了ifAllGranted,所以只有同时拥有ROLE_MOTORISE和ROLE_VIP两个权限的用户才能查看包含在这个标签内的内容。不过这样似乎有点苛刻,毕竟所有的用户都拥有ROLE_MOTORIST权限,而其中的一小部分才有ROLE_VIP权限。因此,我们可以使用ifAnyGranted:
<authz:authorize ifAnyGranted=”ROLE_MOTORIST, ROLE_VIP”>
Welcome Motorist! <br/>
<a href=”j_acegi_logout”>Logoff</a>
</authz:authorize>
这个例子中若要显示欢迎信息或注销连接,用户需要具有ROLE_MOTORIST或ROLE_VIP权限。需要指出的是,如果只有一个单一权限,那么使用ifAllGranted或ifAnyGranted则都一样。
最后一个属性是ifNotGranted。它表明如果用户没有权限列表中的权限时显示标签内容。比如,我们希望使用此属性来阻止任何匿名用户访问内容:
<authz:authorize ifNotGranted=”ROLE_ANONYMOUS”>
<p>This is super-secret content that anonymous users aren’t allowed to see.</p>
</authz:authorize>
在实际场景中,这三个属性通常都是一起使用。例如:
<authz:authorize ifAllGranted=”ROLE_MOTORIST”
ifAnyGranted=”ROLE_VIP, ROLE_FAST_LANE”
ifNotGranted=”ROLE_ADMIN”>
<p>Only special users see this content</p>
</authz:authorize>
上面的代码表明只有用户拥有ROLE_MOTORIST权限,并且拥有ROLE_VIP或ROLE_FAST_LANE权限,而且没有ROLE_ADMIN权限时标签内容才会显示给他。
控制用户所见只是Spring JSP标签库能做的一个方面。下面让我们看看Spring如何使用标签来显示一个合法用户信息。
2. 显示用户验证信息
现在让我们来把刚才的欢迎信息加上用户姓名。用户的登录信息总是被封装在一个对象中。该对象可以通过Authentication.getPrincipal方法获得。我们需要做的就是寻找一个能够在JSP中访问principal对象的方法——<authz:authentication>标签。
<authz:authentication>标签能够提供从Authentication.getPrincipal返回并要输出到JSP的对象属性。Authentication.getPrincipal方法通常返回UserDetails接口的一个实现。该接口有个getUsername方法。因此,若要显示UserDetails对象的username属性,我们需要添加下面的标签代码:
<authz:authorize ifAnyGranted=”ROLE_MOTORIST, ROLE_VIP”>
Welcome <authz:authentication operation=”username”/>
</authz:authorize>
operation属性容易引起误解,似乎是调用一个方法。其实,它只是调用了一个getter方法来获取指定名字的属性。默认情况下,operation值的第一个字母都要大写,这样在其前面加上get就可以产生被调用的方法名。本例中,getUsername方法就会被调用并且输出返回值。