1. 程式人生 > >SP+Servlet+JavaBean+Filter+JSTL小例子

SP+Servlet+JavaBean+Filter+JSTL小例子

web應用:MVC使用者認證部分    下載原始碼


〖 作者: island 〗〖 大小:808k 〗〖 釋出日期:2007-09-08 〗〖 瀏覽:1 〗
 (一)    學習java web應用已經快有一個月了,基本上已經掌握了一些基本的皮毛。所以一個階段性的小編成實戰就是必須的了。
為了便於對知識的鞏固,所以決定選取一個比較簡單的基於MVC模式的網站使用者認證例項。這個小的應用主要用到了JSP+Servlet+JavaBean+Filter+JSTL。

    我的執行環境仍然是tomcat5.5+jdk1.5+mysql4.1
廢話一下: JSP主要是用於做頁面展示的,javabean主要是用來為資料庫訪問做服務的,而servlet主要就是處理請求,頁面轉發之類的。詳細的應用,我們在設計到的時候會說明的。
好了,開始動手!
首先是做一個驗證使用者的資料庫。用於儲存使用者名稱及密碼。
sql程式碼如下:
(為了便於操作訪問資料庫所使用的是root使用者,密碼設為空) 
----------------------------------------------------------
use mysql
create database new_db;
create table user
(
 username varchar(24)
  primary key,
  password varchar(20)
);
insert into user values ('island','Mobile');
insert into user values ('bodao051','871016');
----------------------------------------------------------
好了資料庫已經建好了
我們可以試著查詢一下看看資料庫是否正確
----------------------------------------------------------
use mysql;
use new_db;
select * from user;
----------------------------------------------------------
確定自己的資料庫成功建立之後,就開始後面的工作了。執行圖:
  
首先我們需要一個前端的展示。我們採用JSP。
我們預設index.jsp為首頁。而login.jsp為頁面用來展示登陸的標單。

我們採用這樣的模式來處理使用者的登陸的判斷:
首先頁面welcome-file是設定為index.jsp。這個頁面裡面包含了一個認證是否成功登陸的JSTL程式碼,判斷的方法就是如果user的session範圍的物件是空的話,轉發至login.jsp進行登陸。如果不為空則顯示已經成功登陸。
index.jsp的程式碼如下:
---------------------------------------------------------
index.jsp
---------------------------------------------------------
<%@page contentType="text/html; charset=GBK"%>
<

%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="user" value=""/>
<html>
<head>
<title>index</title>
</head>
<body>
<center>
  <h1>使用者管理</h1>
  <br/>
  <c:if test="true">對不起,您還未登入!請點選<a href="pagecontrol?action=out">這裡</a>登陸。</c:if>
  <c:if test="false">
   <p><b></b> 使用者,您好,您已經登陸本站!&nbsp;</p>
 <p>若重新登入,請<a href="pagecontrol?action=out">登出</a></p> 
 </c:if>
</center>
</body>
</html>
---------------------------------------------------------
這段程式碼中涉及到這麼一句程式碼
<a href="pagecontrol?action=out">
這個index.jsp的精華所在。
這個連結是和一個叫做CheckServlet.java的servlet相連的。這個servlet的作用就是用來處理Http的請求即驗證使用者是否輸入正確的。稍後會重點介紹。
接著我們再作一個login.jsp用來進行使用者的表單的登陸。
為了簡單起見我們只作一個比較簡單的頁面。
程式碼如下:
----------------------------------------------------------
index.jsp
----------------------------------------------------------
<
%@page
contentType="text/html; charset=GBK"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>login</title>
<script language="JavaScript1.2">
function submitForm(theForm)
{
 if(theForm.username.value==""){
           alert("請輸入使用者名稱");
           theForm.username.focus();
           return false;
    }else if(theForm.password.value==""){
           alert("請輸入密碼");
           theForm.password.focus();
           return false;
    }else return true;
}
</script>
</head>
<body bgcolor="#ffffff">
<center>
  <h1>登入頁面</h1>
 <form name="form1" method="post" action="checkservlet" onsubmit="javascript:return submitForm(this);">
  <br>
  <br>
  <table align="center">
    <tr><td>使用者名稱</td><td><input type="text" name="username" value=""/></td></tr>
    <tr><td>密碼</td><td><input type="password" name="password" value=""/></td></tr>
    <tr>
      <td>
        <input type="submit" name="Submit" value="登陸">
        &nbsp;&nbsp;
        <input type="reset" value="重置">
      </td>
    </tr>
  </table>
  </form>
</center>
</body>
</html>
----------------------------------------------------------
從這句程式碼中
<form name="form1" method="post" action="checkservlet" onsubmit="javascript:return submitForm(this);">
我們可以看出,表單的結果是以post的方式提交到checkservlet的

到這裡我們的展示基本上已經結束,可以得到展示層都和checkservlet這個檔案有著很大的關係。我們下一步的任務就是搞清楚checkservlet的用途和怎麼寫這個servlet。
(二)我們由展示層提交上來的HttpServletRequest,到底該怎麼處理?
    我們猜想既然這是一個驗證使用者登陸認證的MVC模式例子,那麼請求所涉及的使用者名稱和密碼就必然需要一個驗證的機制,簡單的理解就是我們需要和資料庫取得連線,然後從資料庫那兒比較使用者的請求是否是合法的。思路基本上就是這樣,可是我們該怎麼去實現它呢?於是CheckServlet.java就應運而生啦!
好了,先讓我們看一下CheckServlet.java的程式碼,再去理解它為什麼要這樣去寫。
----------------------------------------------------------
CheckServlet.java
----------------------------------------------------------
package com.teach.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import com.teach.bean.*;
import java.io.*;
public class CheckServlet extends HttpServlet
{
 private static final long serialVersionUID = 1L; //為保持版本相容性附給物件的唯一識別符號
 // 在這裡初始化全域性變數
    public void init()  throws ServletException {}
   
    //這裡處理HTTP的GET請求
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws  ServletException, IOException
    {
        if (request.getParameter("username") != null &&
             request.getParameter("password") != null)
        {
            String userName = request.getParameter("username");
            String userPwd = request.getParameter("password");
           
            SQLBean db = new SQLBean();
            UserBean ub=db.checkUsersLogin(userName, userPwd);
            db.close();
            request.getSession().setAttribute("user",ub);
            response.sendRedirect("index.jsp");
        }
    }
    //  處理HTTP的POST請求
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws
        ServletException, IOException
    {
        doGet(request, response);
    }
    //在這裡清理資源
    public void destroy(){}
}
---------------------------------------------------------
從這裡我們可以得知我們checkservlet首先從HttpServletRequest中取出兩個元素一個是使用者名稱"username",一個是密碼"password",取出這兩個元素之後下一步需要做的就是進行驗證,思路很簡單將這兩個元素與我們所建立的資料庫裡面所儲存的紀錄進行比較,如果資料庫中含有請求的兩個元素的紀錄則使用者為合法的,反之則為非法使用者,對於非法使用者之後的處理我們這個例子先不涉及,以後會逐漸的完善的。
呵呵,下一個步驟就很清楚了,和資料庫取得連線。
我們把對資料庫處理的程式碼放在Bean裡面。
首先呢,我們需要訪問new_db這個資料庫,步驟是規定好的。
第一步裝載,註冊資料庫的JDBC驅動程式。
第二步就是建立與資料庫的連線。
第三步就是建立statement物件,準備要呼叫的SQl語句。
第四步呼叫SQL語句。
第五步訪問ResultSet中的紀錄集。

最後一步一次關閉ResultSet,statement,Connection物件。
之所以詳細的列出這些步驟,是因為這些步驟都是規定,必須一步一步完成。是很重要的!
我們把連線資料庫德步驟和判斷使用者合法性的方法封裝在一個Bean裡面,取名叫作SQLBean.java
----------------------------------------------------------
SQLBean.java
----------------------------------------------------------
package com.teach.bean;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SQLBean {
 
    private Connection conn = null;
    private ResultSet rs = null;
    private java.sql.PreparedStatement pstmt = null;
   
 public SQLBean() {
  try
  {
   //連線MySQL資料庫的字串
   String cstr="jdbc:mysql://localhost:3306/new_db?useUnicode=true&characterEncoding=GBK";
   //裝載MySQL資料庫的驅動程式
   Class.forName("com.mysql.jdbc.Driver").newInstance();
   //建立連線
   conn= java.sql.DriverManager.getConnection(cstr, "root","");
     }
  catch(SQLException e){ System.out.println(e.getMessage()); }
  catch(Exception ex){ System.out.println(ex.getMessage());}
 }
    public UserBean checkUsersLogin(String userName, String userPwd) //登陸驗證
    {
        UserBean ub = null;
        if (!checkParameter(userName + userPwd))
        {
            userName = "null";
            userPwd = "null";
        }
        try
        {
            String sql =
                "select count(*) from user where username=? and password=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, userName);
            pstmt.setString(2, userPwd);
            rs = pstmt.executeQuery();
            if (rs.next())
            {
                if (rs.getInt(1) > 0)
                {
                    ub = this.getUser(userName);
                }
                else
                {
                    ub = null;
                }
            }
        }
        catch (Exception e)
        {
            ub = null;
            e.printStackTrace();
        }
        return ub;
    }
    public boolean checkParameter(String para) //過濾非法字元
    {
        int flag = 0;
        flag += para.indexOf("'") + 1;
        flag += para.indexOf(";") + 1;
        flag += para.indexOf("1=1") + 1;
        flag += para.indexOf("|") + 1;
        flag += para.indexOf("<") + 1;
        flag += para.indexOf(">") + 1;
        if (flag != 0)
        {
            System.out.println("提交了非法字元!!!");
            return false;
        }
        return true;
    }
   
    public UserBean getUser(String userName) //提取登陸使用者資訊
    {
        UserBean ub = new UserBean();
        String sql = "select * from user where username=?";
        try
        {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, userName);
            rs = pstmt.executeQuery();
            while (rs.next())
            {
                ub.setUserName(rs.getString("username"));
                ub.setPassword(rs.getString("password"));
                ub.setUserId(rs.getString("userid"));
                ub.setTrueName(rs.getString("truename"));
                ub.setUserType(rs.getString("usertype"));
            }
        }
        catch (SQLException ex)
        {
            ex.printStackTrace();
        }
        return ub;
    }
   
 //關閉resultset、statement和connection
 public void close() 
 {
   try{
  if(rs!=null) rs.close();
  if(pstmt!=null) pstmt.close();
  if(conn!=null) conn.close();
   }catch(SQLException e){ System.err.println(e.getMessage());}
 }
}
----------------------------------------------------------
(三)這樣的話,我們只要在checkservlet裡面對SQLBean宣告一個例項就可以完成一些列的操作啦!
    好了,如果我們的使用者是合法的使用者,那麼我們就需要將這個使用者的資訊作為一個物件儲存起來,具體的所就是通過Request裡的getSession()方法獲取與客戶端請求相聯絡的會話即session,在通過session中的setAttribute()方法將物件的值賦給該session變數。並通過Respone裡的sendRedirect()方法進行重新定向,我們這裡把index.jsp作為了重新定向的Location。
這樣的話,我們在回頭去看index.jsp就明白為什麼要從session裡面取值是否為空來判斷使用者是否合法了。
我們設定怎麼一個用於儲存使用者物件,並通過對它的判斷來得知使用者的合法性,具體的是非空就合法,空就非法。剛才的SQLBean就用到了。這個很簡單我們定義一個名為Userbean.java的Bean
----------------------------------------------------------
UserBean.java
----------------------------------------------------------
package com.teach.bean;
/*
使用者Bean
*/
public class UserBean
{
    private String _userId;
 private String _userName;
    private String _password;
    private String _trueName;
    private String _userType;
   
    public UserBean(){}
    public void setUserId(String userId)
    {
        _userId = userId;
    }
   
    public void setUserName(String userName)
    {
        _userName = userName;
    }
    public void setPassword(String password)
    {
        _password = password;
    }
    public void setTrueName(String trueName)
    {
        _trueName = trueName;
    }
   
    public void setUserType(String userType)
    {
        _userType = userType;
    }
   
    public String getUserId()
    {
        return _userId;
    }
   
    public String getUserName()
    {
        return _userName;
    }
    public String getPassword()
    {
        return _password;
    }
   
    public String getTrueName()
    {
        return _trueName;
    }
   
    public String getUserType()
    {
        return _userType;
    }
}
----------------------------------------------------------
呵呵,這個Bean似乎方法有點多,有人說我們只需要使用者名稱和密碼就可以了。對!這個是為了以後我們考慮到資料的全面性。比如使用者的真實姓名,使用者的ID等等。
     我們似乎很滿意自己的思路,可是問題是,我們是用什麼方法完成頁面轉發的呢?
很多人知道,在MVC的框架模式中,Servlet通常作為整個web應用的控制器來使用。大部分的請求資訊都是遞交給Servlet來處理,Servlet再根據不同的請求轉發到不同的JSP。
     我們回頭看一下login.jsp。看這個程式碼片斷<form name="form1" method="post"
我們的請求是以post的方式傳遞出去的。所以我們應該有個dopost的方法來處理這些請求。於是我們利用ControlServlet來進行對請求資訊的處理。
----------------------------------------------------------
ControlServlet.java
----------------------------------------------------------
package com.teach.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
//import com.teach.bean.UserBean;
public final class ControlServlet extends HttpServlet {
 
 private static final long serialVersionUID = 1L; //為保持版本相容性附給物件的唯一識別符號
 
 protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
        {
            String action = request.getParameter("action");
            String jspPage = "index.jsp";
           
            if ((action == null) || (action.length() < 1))
            {
                action = "default";
            }
            if ("default".equals(action))
            {
                jspPage = "index.jsp";
            }
            else if ("out".equals(action))
            {
                request.getSession().invalidate();
                jspPage = "login.jsp";
            }
         
            dispatch(jspPage, request, response);
        }
        protected void dispatch(String jsp, HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
        {
            if (jsp != null)
            {
                RequestDispatcher rd = request.getRequestDispatcher(jsp);
                rd.forward(request, response);
            }
        }
        protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
        {
            doPost(request, response);
        }
}
----------------------------------------------------------
(四)
    ControlServlet繼承了HttpServlet,並對其dopost方法進行了重寫。我們分析一下這個重寫的dopost()方法。
首先login.jsp表單的結果是由CheckServlet來處理的,action用來儲存處理的資訊,我們可以知道如果該使用者是非法的,那麼action就會被賦值out,如果使用者是合法的,我們通過對CheckServlet的瞭解知道,頁面被重新定向到index.jsp,我們看一下index.jsp的程式碼,注意到有這麼一段

<c:if test="false">
   <p><b></b> 使用者,您好,您已經登陸本站!&nbsp;</p>

呵呵,可以知道此時的action的值為預設的null
那麼可以很明白的知道下一步肯定就是對action值的判斷,沒錯!
從ControlServlet的dopost方法可以看出,如果action的值是null那麼頁面轉發至index.jsp頁面即回到首頁顯示“使用者,您好,您已經登陸本站!”;如果action的值是out那麼頁面轉發至login.jsp要求使用者重新登陸。
這樣我們就基本上掌握了這個簡單的MVC模式的使用者認證例項了。
呵呵,或許還有朋友會問,我們設定的welcome-file是index.jsp啊,為什麼事實上我們一開始進入的是login.jsp呢?
呵呵,很細心哪,其實我們還漏了一個很重要的東西!
對了,就是Filter(過濾器技術),這個可是Servlet2.3新增的功能哪!很重要的!千萬別把它和servlet混為一談哦!
它的作用是:在Request到達Servlet和JSP之前進行預處理,或者在離開Servlet或JSP時處理返回給客戶端的Response。呵呵,很難懂?沒事,通過下面的分析或許會有個感性的認識。
    通過上面的理解我們知道這個應用的Request請求主要是"user",要知道電腦的速度是很快的,我們剛訪問我們的應用的時候確實是先進入的index.jsp而且這個時候生成的request,在這個request被傳遞到servlet或Jsp之前的時候神奇的惡filter出現了,它攔截了Request,並通過getRequestURI()方法獲取該Request將要傳遞的目的地,而且通過httpRequest.getSession().getAttribute("user")方法獲得session變數的值,並將該值強制轉換為UserBean的一個例項。下面就是判斷過程了。

如果這個例項是空的話,分兩種情況討論:
1,若這個request的目的地與login.jsp或者checkservlet或者reg.jsp有關的話那麼就讓這個request該區哪兒去哪兒。否則的話就把它強制傳遞到login.jsp。想想也對,如果request中的user的值為空的話,那麼就說明我們還沒有登陸,下一步肯定是要去login.jsp的。

2,如果這個request中已經包含"user",根據我們上面的思路則認為已經合法登陸,所以filter也不會插手這個request,愛去哪兒就去哪兒。
而我們的實際情況是:我們第一訪問這個應用的時候都是還沒有登陸的。所以雖然我們設定的welcome-file是index.jsp可是我們還是要進入login.jsp進行登陸的。而filter就幫助我們解決了這個問題。因為計算機的速度問題,我們是察覺不到剛開始時的頁面是index.jsp。除非我們的機器慢得像蝸牛一樣!呵呵!所以呢,我們一下子就進入了login.jsp。這個就是filter的作用啦!
明白了吧?
呵呵我們給這個filter取名為:LoginFilter.java
----------------------------------------------------------
LoginFilter.java
----------------------------------------------------------
package com.teach.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import com.teach.bean.*;
/*
過濾器servlet 用於判斷使用者是否登陸成功
*/
public class LoginFilter extends HttpServlet implements Filter {
 private static final long serialVersionUID = 1L; //為保持版本相容性附給物件的唯一識別符號
 
 //private FilterConfig filterConfig;
   
 public void init(FilterConfig filterConfig) throws ServletException {
        //this.filterConfig = filterConfig;
    }
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) {
          HttpServletRequest httpRequest=(HttpServletRequest)request;
                HttpServletResponse httpResponse=(HttpServletResponse)response;
                try
                {
                   request.setCharacterEncoding("GBK");
                   String url= httpRequest.getRequestURI();
                   UserBean ub=(UserBean)(httpRequest.getSession().getAttribute("user"));
                   if(ub==null)
                   {
                       if(url.indexOf("login.jsp")!=-1||url.indexOf("checkservlet")!=-1||url.indexOf("reg.jsp")!=
                            -1||url.indexOf("regservlet")!=-1)
                       {
                           filterChain.doFilter(httpRequest, response);
                       }else
                       {
                            httpResponse.sendRedirect("login.jsp");
                       }
                   }else
                    filterChain.doFilter(httpRequest, httpResponse);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
    }
    public void destroy() {}
}
----------------------------------------------------------
大家覺得這個filter是不是很酷呢!
呵呵!
     如果我們是這個web應用的管理員或者說是開發者,我們很想知道使用者每次對我們的頁面進行訪問請求時的情況,比如說請求的地址,請求的時間,請求被執行的時間,和關於這次請求的報告。這對於開發者或者管理者是很重要的!
呵呵,這個時候我們可愛的filter又起作用了
我們可以編寫一個日誌Filter。來記錄我們這個使用者認證應用的訪問情況。命名為:LogFilter.java
----------------------------------------------------------
LogFilter.java
----------------------------------------------------------
package com.teach.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;
public class LogFilter  extends HttpServlet implements Filter {
 private static final long serialVersionUID = 1L; //為保持版本相容性附給物件的唯一識別符號
 FilterConfig filterConfig;
 public void setFilterConfig(FilterConfig filterConfig) {
  this.filterConfig = filterConfig;
 }
 public FilterConfig getFilterConfig() {
  return filterConfig;
 }
 public void init(FilterConfig filterConfig) throws ServletException  //初始化
 {
  setFilterConfig(filterConfig);
 }
 
 
// 日誌過濾器的doFilter方法
 public void doFilter(ServletRequest request, ServletResponse response,
                          FilterChain filterChain)throws ServletException,IOException
 {
  HttpServletRequest httpRequest=(HttpServletRequest)request; //獲得HTTP請求的物件
  ServletContext context = getFilterConfig().getServletContext(); //獲得過濾器配置環境
  String filterName = getFilterConfig().getFilterName();  //獲得過濾器的名字
  long bef = System.currentTimeMillis();
  filterChain.doFilter(request,response);
  long aft = System.currentTimeMillis();
  context.log(httpRequest.getRemoteHost() +"請求地址"
   +httpRequest.getRequestURL() +"在" + new Date() + ",執行時間:" +(aft-bef)
   +" (" +filterName + "報告.)");
 }
}
----------------------------------------------------------
至此我們的MVC使用者認證的簡單應用就算是告一段落了!
是不是覺得這個例項確實用到了很多東西呢?
呵呵,麻雀雖小五臟俱全!

哦,忘了,我把這個應用的web.xml貼出來吧。
----------------------------------------------------------
web.xml
----------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"  version="2.4">
 <description>teach</description>
 
  <filter>
    <filter-name>LoginFilter</filter-name>
    <filter-class>com.teach.servlet.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>LoginFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>
 <filter-name>Logger</filter-name>
 <filter-class>com.teach.servlet.LogFilter</filter-class>
  </filter>
  <filter-mapping>
 <filter-name>Logger</filter-name>
 <url-pattern>/*</url-pattern>
  </filter-mapping>
   
   <servlet>
      <servlet-name>CheckServlet</servlet-name>
      <servlet-class>com.teach.servlet.CheckServlet</servlet-class>
   </servlet>
  <servlet>
    <servlet-name>PageControl</servlet-name>
    <servlet-class>com.teach.servlet.ControlServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CheckServlet</servlet-name>
    <url-pattern>/checkservlet</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>PageControl</servlet-name>
    <url-pattern>/pagecontrol</url-pattern>
  </servlet-mapping>
    <welcome-file-list>
 <welcome-file>
     index.jsp
 </welcome-file>
    </welcome-file-list>
</web-app>
----------------------------------------------------------
這樣的話,執行就應該沒問題了,啟動tomcat伺服器輸入http://localhost:8080/island5/
就可以運行了,當然,前面的建立資料庫仍然是必要的!呵呵!