`
kyo100900
  • 浏览: 634275 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【JSF专家Dennis Byrne】JSF反模式与陷井(三)完

阅读更多

终于到本文的最后一节了,本节内容主要讲JSF也是面对接口编程的,作者举了个简单的例子。然后在JSF安全问题上,使用了类似于ASP.NETviewstate概念的东东。最后谈到了头痛的Portlet程序开发问题,作者指出Portlet问题远不止这些,而且问题都出在Portlet本身,非常同情Portlet开发人员。

基于接口编程

JSF鼓励“包含与扩展”的设计原则。因此JSF组件模型广泛基于接口。下面的这个例子中,“ImplementationDependentManagedBean.java”却没有使用接口。好吧,我需要对它做些小小的修正。

import org.apache.myfaces.component.html.ext.HtmlInputHidden;
import org.apache.myfaces.component.html.ext.HtmlInputText;
import org.apache.myfaces.component.html.ext.HtmlOutputText;

public class ImplementationDependentManagedBean {

	private HtmlInputText input ;
	private HtmlInputHidden hidden ;
	private HtmlOutputText output ;

	/* 省略getters和setters方法 */

	public boolean recordTotal(ActionEvent event) {

		long total = ((Long)input.getValue()).longValue();
		total += ((Long)hidden.getValue()).longValue();
		total += ((Long)output.getValue()).longValue();

		return new JmsUtil().broadcastTotal(total);
	}

}

 

  

这个bean绑定了三个组件作为其属性,业务逻辑部分将这三个组件封装到一个action listener里去了。(action listener指的就是这里的recordTotal方法)。这三个类(这里指的是HtmlInputTextHtmlInputHiddenHtmlOutputText)都是MyFaces包自带的。这段代码中需要注意的是这个类的业务逻辑部分recordTotal方法只会调用每个组件的getValue方法。而通过JSFAPI,你又会发现这些组件的getValue方法又承继于它们的父类。因此我决定要重构一下代码,将这几个导入的包给换掉:

import javax.faces.component.ValueHolder;

public class RefactoredManagedBean {

   private ValueHolder input ;
   private ValueHolder hidden ;
   private ValueHolder output ;

   /* getters & setters ommitted */

   public boolean recordTotal(ActionEvent event) {

    long total = 0;

    for(ValueHolder valued : new ValueHolder[] {input, hidden, output})
         total += ((Long)valued.getValue()).longValue();

    return new JmsUtil().broadcastTotal(total);

   }
}

 

 

现在我要说一下,ValueHolderHtmlInputText, HtmlInputHidden HtmlOutputText父类实现的一个接口。通过这次重构,我们只要一个ValueHolder接口就可完成原本需要三个类(上面提到的HtmlInputText, HtmlInputHidden HtmlOutputText)才能完成的工作,代码也干净整洁。你会发现使用Polymorphism(多态)对于基于接口编程的业务逻辑代码整理是相当优雅的。(注:不论是JDK本身还是,目前主流的开源框架,接口,多态运用的非常广泛)

 

请记住,JSF的优点之一就是让你自由自在的在POJO里编写的你的业务逻辑,开发人员并不要实现任何接口或继承任何类!这一点并没有意味着我们失掉了面向对象的编程原则。当你有机会在基于标准的接口上编程的话,大胆去做吧。

 

最后,你有没有注意到recordTotal这个action listener JSF规范规定Actionlistener方法返回值要为void,但咱们这里却返回一个boolean。实际上MyFaces相对规范来说,宽松了点,但允许你这么做,但实际上还是会忽略返回值的。但尽量还是避免这样,因为根据JSF的实现参考来说,有返回值是应该抛异常的,也许别的JSF实现可能与MyFaces不同了吧。

 

 

View State(视图状态)加密

一个普遍的错误观念就是许多开发人员觉得View State加密根本不需要SSL很显然嘛,SSLView State加密根本没有共同点——它们在各自不同的网络协议中解决属于各自的问题,互不相干。

 

好,我们来再来举个例子。假设Manfred是个银行,Sean是个在线客户,而Mike是媒介。在使用普通的HTTP的情况下,Mike可以截取Sean发送给Mannfred的请求,偷偷记下Sean的密码,然后再转发给Manfred。这一系列动作对于ManfredSean任何一方来说,都是神不知,鬼不觉的。

 

 

 

如果在网络传输层上ManfredSean使用了SSL的话,就可以有效阻止Mike窃取他们之间的信息。

 SSL提供的是点到点的安全保障,这样从Sean发送到Manfred的请求就不会被Mike截取了。但如果应用程序在客户端保存状态的话,SSL就法保证一定是安全的了。当使用客户端状态保存时,JSF会创建一个数据结构,它是一个控件树的序列化Base64编码,用来表示绑定组件的响应状态值,这个数据结构就是view state,并且此时view state是被序列化和加密过的,然后隐藏在的一个HTML hidden字段中。 HTML表单提交时,它会将view state的值作为一个HTTP参数传到服务器后台。JSF利用该参数的值,进行反序列化,解密等逆向操作来还原先前的视图是的状态值。这对所有的JSF实现来说都是一个很大安全性挑战,因为Sean是可以自由的改变view state值的(因为它只是一个隐藏的hidden字段)。 我强烈推荐在你的产品中使用view state加密,因为这样可以防止web客户端被恶意篡改view state的值。我也建议在开发和功能测试阶段关闭view state加密(因为Base64编码后的数据量很大,反序列化或解密操作估计有性能损耗)。要想使用view state加密,去好好看看你使用的JSF实现文档吧。

 

 

Portlet问题

我有点同情Portlet开发人员。确实,他们总是在邮件列表或论坛上不断的提出一个接一个问题,而这些问题本身来自Portlet,与他们无关。如果一定要说有这么一批人不得不对标准低头,被迫屈服的话,那非Portlet开发人员莫属了。(同情中………………

 

有些JSF API在基于Portlet的应用程序中表现的行为是不同的。如果你的Portlet应用程序运行在一个普通的Servlet容器里,那么有一些假设需要去避免。现在假设下面的这段代码在Servlet容器里运行的好好的:

 

FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext externalContext = ctx.getExternalContext();
ServletRequest request = (ServletRequest) externalContext.getRequest();
String id = request.getParameter("id");

 

 

如果现在你想运行Portlet应用程序,就必须取消显式的javax.servlet.ServletRequest or javax.servlet.ServletResponse进行转换。在Portlet应用程序中,ExternalContext.getRequest 返回的是一个javax.portlet.PortletRequest,结果可想而知,当然会抛出ClassCastException异常。我这里有一个通用的好办法,也可以获取request中的属性值,请看:

 

FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext externalContext = ctx.getExternalContext();
externalContext.getRequestParameterMap().get("id");

 

 

 

(全文完)

9
7
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics