欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JSF专家Dennis Byrne】JSF反模式与陷井(三)完 博客分类: 专家文章翻译 JSF设计模式编程EXT网络应用 

程序员文章站 2024-02-19 20:50:04
...

终于到本文的最后一节了,本节内容主要讲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任何一方来说,都是神不知,鬼不觉的。

 

【JSF专家Dennis Byrne】JSF反模式与陷井(三)完
            
    
    博客分类: 专家文章翻译 JSF设计模式编程EXT网络应用 

 

 

如果在网络传输层上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");

 

 

 

(全文完)