前端控制器处理所有从用户过来的请求。所有用户的请求都要通过前端控制器,只有在得到控制器验证后,被前端控制器重新定向才可以访问相应的资源。这是安全控制的一种方式。实际上在,在J2EE体系结构下,各种资源的联结都是通过XML实现的,所有web层应用的文件要部署时候必须打包成war文件,web文件通常是一些jsp文件,也有一些servlet和 javabean以及jsp标签。在简单的WEB应用中,可以在jsp文件中实现对业务方法,甚至后台数据库资源等实现直接访问。但是作为一个可扩展和可伸缩的应用来说,这样做有很多不便。这在MVC模式分析与应用时我会有更详实的论证。这里要集中研究的是前端控制器模式在应用中是怎么实现的。先来看看Front Control 的类图[i]:
任何模式都有应用的上下文环境(context),在前端控制器模式的应用环境可以分布式的处理,图中控制器虽然只有一个,实际中有可能是多个控制器分布式地来处理不同的控制要求。这种分布式控制机制是由WEB的配置文件web.xml来实现的。当然也可以集中处理所有请求,在一个集中的环境中判断分发请求。可以实现一个链式的结构。这其实就是GOF中提的Chain of Responsibility模式了,进一步映射到J2EE模式目录中,严格的讲在控制前面我们应该采取过滤器模式,这样前端的验证可以得到更好的处理,但是这样阶梯的处理容易造成低效率。因为判断链的方式需要花费时间。前面说过分布式控制用户的请求是由配置文件描述的,下面是我开发的一个事例程序中的WEB配置文件:
1<web-app>
2 <servlet>
3 <servlet-name>logincheck</servlet-name>
4 <servlet-class>frontcontrolleraa.logincheck</servlet-class>
5 </servlet>
6 <servlet-mapping>
7 <servlet-name>logincheck</servlet-name>
8 <url-pattern>/logincheck</url-pattern>
9 </servlet-mapping>
10 <servlet>
11 <servlet-name>loginfront</servlet-name>
12 <jsp-file>/login.jsp</jsp-file>
13 </servlet>
14 <servlet-mapping>
15 <servlet-name>loginfront</servlet-name>
16 <url-pattern>/test/*</url-pattern>
17 </servlet-mapping>
18</web-app>
当有客户端请求时,容器都要读取配置文件以便判断用户对资源的访问权限,并对某些类型的用户分发到对应的控制器来处理。应用中16行意思是规定在上下文环境下(开发目录)的子目录下test下的所有文件必须交给一个名字叫loginfront的servlet来处理。在11行中把这个servlet映射到了一个jsp文件(大家知道jsp本质上是servlet)。在login.jsp文件其实只是一个用户界面,他把获取的请求交给名为logincheck的servlet(在配置中应有相应的描述)判断——典型的视图(jsp实现)-控制器(servlet实现)模式(<form method="post" action="/test/logincheck">)。这样就实现了对test目录下资源的保护。很显然一个保护天平的一端是实现了对整个被访问资源的保护,另一端则是对某个资源进行了保护,实现了控制访问。这种保护方式对于在每个资源中加入控制机制要可扩展的多,在用户角色改变时,代码也根本不要做改变,所做的工作只是要改变配置文件而已。代码的维护变得相对独立,在开发业务逻辑的代码时候尤其重要,因为我们不需要牵涉到其他的事情了而集中处理业务逻辑。当然控制器的实现可以有多种策略,这里采用的是servlet策略。核心代码如下:
package frontcontrolleraa;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class logincheck extends HttpServlet {
static final private String CONTENT_TYPE = "text/html; charset=GBK";
//Initialize global variables
public void init() throws ServletException {
}
//Process the HTTP Get request
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//用doPost服务
doPost(request,response);
}
//Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
//得到用户名和密码
String id;
String passwd;
id=request.getParameter("userName").trim();
passwd=request.getParameter("password").trim();
/*
判断用户和密码合法性和效用(决定可以访问的资源Dispatcher View)
在此处如果逻辑更多,可以交由Help处理(Help view模式),可以分发到其他控器验证(Intercepting Filter)。
由于只是演示前端控制器的使用,故此处不用数据库:)
*/
if(id.equals("angus_han")&&passwd.equals("hanchen")){
//合法,转向到可用页面
response.sendRedirect("other.jsp");
}
else
//非法,转向到错误页面,实际应用中也可以直接再次转到登陆页面
//就是说只要简单地返回就可以了
response.sendRedirect("loginErrorPage.jsp");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>logincheck</title></head>");
out.println("<body>");
out.println("<p>The servlet has received a POST. This is the reply.</p>");
out.println("</body></html>");
}
//Clean up resources
public void destroy() {
}
}
关于代码的解释在上面已经也很多注释了,这里就不多做解释了。当然在简单的应用中控制器也可以使用jsp实现,复杂的应用则常结合视图助手(View Helper),过滤器(Intercepting Filter),分发者(Dispatcher View)模式应用。[ii]
再来讨论上面描述[A1] 的反例(一个在一些页面缺乏用户认证的网上找到的商业系统,在这里就不点名了),显然程序开发者注意到了资源的保护,退一步说在一个商业应用中不考虑安全的资源保护简直让人感到毛骨悚然!但是开发者只在部分页面中加入了判断逻辑,这种方式至少有两个缺陷:
首先,代码耦合性强
其次,安全性低
前一个缺陷导致重复的代码,在大量需要保护的页面中加入类似的判断逻辑,在增加业务代码时候也必须考虑到加入本来不需要的判断逻辑,前一个缺陷也导致了代码的维护变得量大而难以扩展,尤其在需要调整大量保护资源类别时,维护者不得不(简直是被迫)深入到每个业务代码中去,然而在使用了web.xml后这种耦合骤然降低为0;
后一种缺陷导致开发者挂一漏万,一不小心就会“忘掉”还有个判断逻辑,有如江堤之蚁穴,“周密”的考虑功亏一篑。在反例中只要知道资源的绝对域名,就可以通过浏览器访问相应的资源(当然有代码保护的页面例外)。但是在此演示中我们可以看到,只要试图访问test子目录下的资源,任何资源,浏览器中都会被重新定位到登陆页面。就是说除非你首先登陆,否则客户端有任何资源请求都应该重新定位到登陆页面,要求用户注册或登陆以便获得相应资源的访问权限。当然,这些安全处理的抽象实现要求在servlet规范[iii]中有明确要求,更详细的多控制器处理也可以参照规范编写。示例代码在WebLogic Server ,Jboss,Apusic调试下全部通过。结果表明他们都对这种规范要求的机制支持的很好。
[ii], http://java.sun.com/blueprints/corej2eepatterns/Patterns/FrontController.html
在《Core J2EE Patterns》中也有关于控制器实现策略的Sample Code
[iii], Servlet-2_3-fcs-spec规范75页
[A1]演示?中