原登录模块的不足
在《JBuilder 2005实战JSP开发》专题中,我们完全用JSP技术实现了一个用户登录模块。它提供了一个登录的页面,在用户登录成功后转向欢迎页面,而登录失败后转向登录失败的页面,并且为每个页面提供了一个错误处理页面,当功能页面发生错误时,以一种友好的方式向用户报告错误。但这个模块还存在一些问题:
问题一:没有在每个JSP页面检验用户是否有登录
Web应用程序的JSP页面几乎都需要在验证用户的身份之后才可以访问,也即必须保证JSP页面处理请求之前用户已经确实登录系统了,否则应该拒绝响应并重定向到登录页面。不然如果一个非法的用户直接通过URL访问JSP页面,就会产生系统安全性问题。
我们举一个例子:如果有一个用户直接通过http://localhost:8080/webModule/welcome.jsp访问welcome.jsp界面,我们必须先判断用户有没有登录(通过查看session中是否有以"ses_userBean"命名的对象),如果已经登录,打开welcome.jsp页面,如果没有登录重定向login.jsp登录页面。
提示:
当然更正规的作法应该是通过web.xml配置文件的<security-constraint>等配置项按角色对Web资源的访问权限进行配置,但这种方式确实是比较麻烦的。所以我们一般采用在每一个请求到达处理程序前判断用户的登录信息的方式处理页面访问权限的问题。 为了达到防止非法用户访问功能页面的目的,你当然可以像劳模一样在每一个需要保护的JSP页面中添加一段如下的判断代码加以解决:
1. <%
2. if(session.getAttribute("ses_userBean") == null)
3. {
4. response. sendRedirect("/webModule/login.jsp");
5. }
6. %>
但对于一个拥有成百上千个需保护JSP页面的大型Web应用程序,在每一个页面中添加这段相同的代码,不但单调乏味,违反面向对象的宗旨,而且容易出现漏网之鱼。在本专题中,我们将通过一个Servlet过滤器轻松漂亮地完成这个任务。
问题二:每次生成login.jsp页面用户列表时都重新访问数据库
一般而言,Web应用程序的用户是不常发生变化的,但login.jsp页面的用户列表却在每次刷新时都从数据库表中获取用户数据并生成下拉列表。假设我们这个Web应用系统的用户数据(密码除外)不发生变化,那么我们就可以在Web应用程序启动时,就将用户Id和用户名下载缓存在Web应用服务器的内存中,这样每次在生成登录页面的用户名下拉框时,就不需要重新到数据库表中获取,而直接从Web应用服务器的内存中获取,在性能和效率上都会有极大的提高。
当然,将用户缓存在Web应用服务器中可能并不是很恰当,而诸如学历、性别、民族等字典表更适合缓存。对于那些不会或很少发生更改的数据、不应该每次都从数据库中获取,因为访问数据库的操作是高代价的操作,需要较多的I/O操作,CPU时间和网络通讯,在可能的情况下,应该尽量减少访问数据库的次数,这也是值对象设计模式的精髓所在。这里我们对用户数据进行缓存只是为了描述一种提高性能的解决思路。
本专题中,我们通过一个自动启动的Servlet改造生成用户列表的功能:在Web应用程序启动时,就通过这个Servlet将用户Id和用户名缓存到Web应用服务器中,生成下拉框的用户数据直接从缓存中读取。
新增的若干功能点
此外,我们还利用Servlet技术向原应用程序中加入两个新的功能作为系统日志模块:
功能一:添加一个Web容器的监听器的Servlet
在本专题中,我们将创建一个Servlet监听器,监听Web容器启动和关闭事件,在事件处理方法中记录系统启动和关闭系统事件的日志,日志以Excel文件格式保存。
功能二:通过一个Servlet下载文件
Servlet通过设置输出文件头参数向客户端返回各种类型的响应,如HTML、XML、WML等文本格式或声音、图像、Excel文件等二进制流格式。此外,还可以通过设置输出文件头参数使Servlet生成一个用于下载的文件。我们将通过一个Servlet下载Web应用程序的系统日志。
事前准备
通过File->New Project...新创建一个名为bookStore的工程,并在工程下创建名为webModule的Web模块,将原《JBuilder 2005实战JSP开发》专题的旧的bookStore工程源码拷贝到新的bookStore的工程目录下覆对应的目录。
此外,你还需要维护bookStore工程的类库,使其包含Oracle驱动器的JAR包和时间标签的JAR包,上一个专题已有提及此处从略。