使用控制器逻辑进行有条件渲染内容
现在,让我们将刚刚用 <authorize> 标签实现的例子改成用 java 代码的方式。为了简洁起见,我们只实现一个例子,但实现基于控制器检查的其它例子是很简单直接的。
添加有条件显示的 Log In 链接
为了替代 Spring Security 的 <authorize> 标签,我们假设在模型数据中有一个 Boolean 变量来标示是否显示显示“ Log in ”链接,而这个变量可以在视图上得到。在传统的 MVC 模式下,视图不了解模型为何拥有这个值,它只需要使用即可。我们使用 Java Standard Tag Library (JSTL) 的 if 标签,连同 JSP EL 表达式来实现有条件显示页面的部分。
- < c:if test = "${showLoginLink}" >
- < c:url value = "/login.do" var = "loginUrl" />
- < li > < a href = "${loginUrl}" > Log In </ a > </ li >
- </ c:if >
现在看一下如下的代码以了解我们如何在控制器中将数据填充到模型中。
基于用户的凭证提供模型数据
我们控制器的对象模型是符合面向对象模式的,所有的 Spring MVC 控制器扩展自一个简单的基类 com.packtpub.springsecurity.web.controller.BaseController 。这使得我们能够将通用的代码放在 BaseController 中,从而应用中所有的控制器都能够访问。首先,我们加入一个方法以从当前的 request 中得到 Authentication 的实现类:
- protected Authentication getAuthentication() {
- return SecurityContextHolder.getContext().getAuthentication();
- }
接下类,我们添加一个方法 填充 showLoginLink 模型数据,此处使用 Spring MVC 的注解方式。
- @ModelAttribute ( "showLoginLink" )
- public boolean getShowLoginLink() {
- for (GrantedAuthority authority : getAuthentication().
- getAuthorities()) {
- if (authority.getAuthority().equals( "ROLE_USER" )) {
- return false ;
- }
- }
- return true ;
- }
这个方法添加了 @ModelAttribute 注解,任何实现 BaseController 的控制器触发时, Spring MVC 将会自动执行此方法。针对 authorize 标签方式的其它显示 / 隐藏功能,我们能够很简单地使用模型数据指令重复这种设计模式。
配置页面内授权的最好方式是什么?
与以前版本的标签库相比, Spring Security 3 <authorize> 标签的主要优势在于移除了很多使用中的困扰之处。在很多场景下,使用标签的 url 属性隔离了授权规则变化对 JSP 代码的影响。可以在以下场景下使用 url 属性:
l 标签限制显示的功能能够通过一个简单 url 明确的声明;
l 标签的内容能够明确的与 URL 隔离。
但是在典型的应用中,使用标签 url 属性的可能性会很低。现实情况下应用会比这个复杂的多,需要更多的相关逻辑才能确定如何渲染页面的各部分。
尽管使用 Spring Security 标签库来声明渲染页面部分的方法很诱人,这种方式基于 sping 的语法以及一些其它的方式(包括 if...Granted 和 access 方法),但是有很多情况下使用它并不是一个好主意:
l Tag 标签并不支持比角色成员更复杂的条件。例如,如果我们的应用的 UserDetails 实现包含了自定义的属性,如 IP 过滤、地理位置等,这些情况使用标准的 <authorize> 都不能支持。
但是,这些可以通过自定义的 JSP 标签或使用 SpEL 表达式来支持。即使如此, JSP 也会直接绑定业务逻辑,而这并不是推荐做法。
l <authorize> 标签必须在每一个使用的页面中引用。这可能会导致本来通用的规则产生潜在的不一致性,而这会在不同的物理界面文件中存在。好的面向对象系统设计建议条件规则只在一个地方存在,并在使用的地方对其进行引用。
可以通过封装并且重用 JSP 页面的部分来减少这类问题的发生(我们通过使用通用的 JSP 头文件已经对此进行了阐述),但是在一个复杂的应用中这个问题在所难免。
l 不能在编译阶段校验规则的正确性。编译期常量能够在典型的基于 java 对象系统中使用, JSP tag 标签需要(典型情况下)硬编码角色名字,而简单的拼写错误很难被察觉。
公平来讲,这样的拼写错误能够很容易地在运行应用的功能测试中发现,但是使用标准的 Java 组件单元测试这样的问题更容易被发现。
我们可以看到,尽管基于 JSP 方式的内容有条件渲染很便利,但是也有一些明显的不足。
所有的这些问题都能够通过在控制器中使用代码推送数据到视图层来解决。另外,在代码中进行高级的授权决定能够享受到重用、编译器检查以及适当分离模型、视图、控制器所带来的好处。