1?引言
??? 通過瀏覽器操作的簡易性和實現(xiàn)的方便性,使得基于B/S結(jié)構(gòu)的Web應(yīng)用模式日漸流行;在Web應(yīng)用環(huán)境中,特別是多角色用戶情況下,不同的用戶擁有的操作權(quán)限不同,用戶的身份識別非常重要。本文對Web應(yīng)用用戶身份識別的難點(diǎn)做了簡單剖析,并從應(yīng)用程序的角度實現(xiàn)了用戶的身份認(rèn)證。
2?用戶身份識別的難點(diǎn)——HTTP協(xié)議的無狀態(tài)性
??? HTTP協(xié)議(即超文本傳輸協(xié)議,Hyper Text Transfer Protocol)是一個應(yīng)用層的網(wǎng)絡(luò)協(xié)議,采用客戶-服務(wù)器模式,用來發(fā)送客戶請求的服務(wù)器上的資源。Web應(yīng)用程序的用戶通過瀏覽器進(jìn)行操作,以瀏覽器作為客戶端采用HTTP協(xié)議同Web服務(wù)器進(jìn)行交互,完成相應(yīng)的工作,由此可見,HTTP協(xié)議是Web應(yīng)用程序的重要基礎(chǔ)之一。
??? 每個Web站點(diǎn)都有一個服務(wù)器進(jìn)程,它在特定TCP端口(默認(rèn)80)上不斷監(jiān)聽,以便發(fā)現(xiàn)是否有瀏覽器向它發(fā)出連接建立請求;一旦監(jiān)聽到連接建立請求并建立了TCP連接之后,瀏覽器就向服務(wù)器發(fā)出瀏覽某個頁面的請求,服務(wù)器接著就返回所請求的頁面作為響應(yīng);發(fā)送完響應(yīng)后,服務(wù)器關(guān)閉TCP連接。服務(wù)器處理下一個請求與上一個請求沒有任何關(guān)系,對于HTTP服務(wù)器而言各個請求都是一樣的,不會保留各次處理時的信息,這就是HTTP協(xié)議的無狀態(tài)性。
??? HTTP的無狀態(tài)性既是優(yōu)點(diǎn),也是缺點(diǎn),協(xié)議原本的設(shè)計在于共享資源,“無狀態(tài)”的設(shè)計較有效率也容易實現(xiàn),很適于它的典型應(yīng)用。但對基于Web的應(yīng)用,卻很不方便,特別是對擁有各種角色、權(quán)限的多用戶系統(tǒng)來說,可能要求對用戶的權(quán)限進(jìn)行區(qū)分,不同部門、不同角色能夠查看的頁面不同,對同一頁面的操作權(quán)限也可能要做區(qū)分,這就要求對用戶的身份進(jìn)行驗證,通過驗證的用戶還要保留其身份標(biāo)識,以維護(hù)對相關(guān)聯(lián)頁面的操作權(quán)限。因為HTTP協(xié)議本身不維護(hù)請求之間的狀態(tài)信息,若僅僅通過協(xié)議本身,服務(wù)器無法判斷各請求之間的關(guān)聯(lián)性,也就是說,我們盡管可以使用戶進(jìn)入Web應(yīng)用的默認(rèn)頁面為登陸頁面,讓用戶輸入用戶登陸名及密碼等信息以實現(xiàn)用戶身份驗證,然后再通過鏈接或其它機(jī)制轉(zhuǎn)到其它相關(guān)頁面,但是如果不采取其它措施,用戶即使不從登陸頁面進(jìn)入也可以通過輸入URL的方式對網(wǎng)站上其它頁面進(jìn)行不受限制的訪問。因而,能夠識別用戶的身份、識別一系列遠(yuǎn)程客戶的請求是從同一個客戶處發(fā)出的,是建立有效的基于Web的應(yīng)用程序的關(guān)鍵。
3?解決之道——善用會話(Session)對象
??? 會話可以認(rèn)為是某個用戶和服務(wù)器之間的一系列相關(guān)的交互,這個交互會持續(xù)一段時間。對于單個用戶而言,會話過程的開始以用戶開始訪問某個網(wǎng)站為標(biāo)志,會話過程的結(jié)束以用戶結(jié)束對該網(wǎng)站的訪問為標(biāo)志。不同的用戶對應(yīng)著不同的會話過程,不同的會話過程之間互不干涉,互不影響。
??? 按對象的術(shù)語講,一個會話可以看作是一個對象,它駐留在服務(wù)器上的應(yīng)用程序中并受應(yīng)用程序的影響。一旦在服務(wù)器上建立了一個會話,一個稱為Session ID 的唯一標(biāo)識符就與此會話相關(guān)聯(lián)。每一個訪問網(wǎng)站的用戶在服務(wù)器端可以擁有自己唯一的一個會話對象,此會話對象為此用戶所獨(dú)享,即此會話對象同用戶有一一對應(yīng)的關(guān)系,用戶只能檢索自己的會話對象,而不能訪問其它用戶的會話對象;并且通過session對象的設(shè)置屬性的方法(setAttribute)可以將其它對象綁定到此會話對象,相應(yīng)的通過session對象的取得屬性的方法(getAttribute)可以檢索綁定到此會話對象的其它對象。
??? 注意到會話是客戶和服務(wù)器之間一一對應(yīng)的聯(lián)系,這一點(diǎn)非常重要,我們可以通過這個聯(lián)系將用戶的身份信息綁定到會話對象上,解決身份認(rèn)證和權(quán)限區(qū)分問題。
4?簡單示例
??? 下面,我們借助session對象,通過采用JSP+JavaBean+數(shù)據(jù)庫的方式實現(xiàn)一個用戶認(rèn)證的簡單示例。
4.1? Web應(yīng)用程序結(jié)構(gòu)
??? Web應(yīng)用的用戶界面結(jié)構(gòu)如下圖所示,用戶首先進(jìn)入登陸頁面 /set/login.htm ,在其中輸入用戶ID和密碼;在 /set/login.jsp中進(jìn)行校驗,若校驗通過,則在session保存用戶身份信息,并根據(jù)用戶所屬部門將頁面導(dǎo)向相應(yīng)部門主頁面,否則,將頁面導(dǎo)向登陸頁面重新輸入用戶ID和密碼;對于需進(jìn)行身份驗證的頁面,從session對象中檢索用戶身份信息,如果能檢索到,說明用戶已經(jīng)得到身份驗證,反之,若檢索不到用戶身份信息,說明用戶還未經(jīng)過身份驗證(可能是通過在瀏覽器地址欄輸入URL的方式直接進(jìn)入該頁的),則將頁面導(dǎo)向到登陸頁面;若通過認(rèn)證的用戶擁有操作此頁面的權(quán)限,則允許操作,否則,顯示權(quán)限錯誤信息。

4.2? JavaBean?
???? 本實現(xiàn)中,需要用到如下幾個JavaBean(特殊的類) :
??? User類:即用戶類,表示一個登陸到網(wǎng)站的用戶,具有用戶編號、密碼、部門、角色等屬性已及對這些屬性進(jìn)行存取的相應(yīng)方法。
??? UserManager類:用戶管理員類,對用戶合法性進(jìn)行校驗。實現(xiàn)代碼如下:
package wxf.set;
import java.sql.*;
public class UserManager{ //用戶管理員類,用來通過查詢數(shù)據(jù)庫對用戶驗證
?????? public User checkUser(String userid,String password){
????????????? /**通過查詢數(shù)據(jù)庫,如果存在用戶編號為userid并且密碼為password
????????????? ?*的用戶則構(gòu)造一個User 類的對象,并對此User類的對象設(shè)置userid,
????????????? ?*department,role 等屬性,然后返回此對象;
????????????? ?*如果不存在編號為userid并且密碼為password
????????????? ?*的用戶則返回null
??????? */
?????? }
}
4.3? 登陸驗證頁面(login.jsp)
<%@ page language="java" %>
<%@ page import="wxf.set.*"%>
<%
? String userid=request.getParameter("userid");
? String password=request.getParameter("password");
? UserManager aUM=new UserManager();
? //校驗用戶編號和密碼,確定是否為合法用戶
User aUser=aUM.checkUser(userid,password);
? if(aUser!=null){ //是合法用戶
? ??? session.setAttribute("user",aUser);? //將aUser對象綁定到session對象
? ??? if(aUser.getDepartment().equals("dp1")){
? ?????????? //如果用戶屬于部門1則轉(zhuǎn)到部門1的主界面
? ?????????? response.sendRedirect(response.encodeRedirectUrl("/set/dp1/index.jsp"));
? ??? }
? ??? else if(aUser.getDepartment().equals("dp2")){
? ?????????? //如果用戶屬于部門2則轉(zhuǎn)到部門2的主界面
? ?????????? response.sendRedirect(response.encodeRedirectUrl("/set/dp2/index.jsp"));
? ??? }
? }
??else{
? ??? //不是合法用戶,則轉(zhuǎn)到登陸界面
? ??? response.sendRedirect(response.encodeRedirectUrl("/set/login.htm"));
? }
%>
4.4? 身份驗證代碼
??? 非法用戶在未通過身份驗證的情況下,可能通過鍵入URL非法訪問某個特定JSP文件,為此,必須對要保護(hù)的頁面安排身份檢查代碼。因為對每個登陸到網(wǎng)站的用戶,都可以擁有自己獨(dú)享的唯一的session對象,每個用戶只能訪問自己的session對象;因此,可以在要求認(rèn)證的JSP文件的開頭安排如下代碼:從session對象中檢索用戶(User)對象,因為用戶如果是從登陸頁面進(jìn)入并且是合法用戶,則在登陸時已經(jīng)將用戶的身份信息封裝成User對象綁定到session對象上了,所以如果能檢索到,說明用戶是經(jīng)過了合法認(rèn)證進(jìn)到該頁面的,否則,說明用戶還未經(jīng)過認(rèn)證,需要將頁面轉(zhuǎn)到認(rèn)證頁面;如果經(jīng)過認(rèn)證,則再從用戶對象中檢索其角色(role)屬性,若允許此角色的用戶訪問該頁,則顯示頁面內(nèi)容,否則,顯示權(quán)限錯誤信息。示例代碼如下:
?????? <%
???? //從session對象中檢索名稱為user的對象
? ???User theUser=(User)session.getAttribute("user");?????
???? if(theUser==null){?????? //如果不能能檢索到用戶對象
? ??? ????response.sendRedirect(response.encodeRedirectUrl("/set/login.htm");
? ???}
??? else{?????? //如果存在用戶對象則取得用戶的角色信息
? ??? ??String theRole=theUser.getRole();
? ??? ??//假使角色為11的用戶可以訪問
? ??? ??if(!theRole.equals("11")){??
? ?????????? out.println("對不起,您無權(quán)訪問此頁!");
? ??? ??}
? ??? ??else{
? ?????????? //顯示頁面內(nèi)容????
? ??? ??}
? ??}
%>
5 總結(jié)
??? Web應(yīng)用的關(guān)鍵是要識別用戶,HTTP協(xié)議的無狀態(tài)性使服務(wù)器無法跟蹤用戶操作的相關(guān)性;JSP內(nèi)建的session對象,是每個登陸到網(wǎng)站的用戶獨(dú)自擁有的,我們可以借助session對象保存用戶的身份信息,對涉及需要特殊身份才能訪問的頁面設(shè)置身份校驗代碼,檢索用戶session對象中的身份信息進(jìn)行校驗,可以較好實現(xiàn)用戶身份識別。
參考文獻(xiàn)?
【1】謝希仁.計算機(jī)網(wǎng)絡(luò)(第二版).北京:電子工業(yè)出版社,1999.4
【2】Karl Avedal, Danny Ayers 等.JSP編程指南.北京:電子工業(yè)出版社,2001
【3】黃理,洪亮,等.JSP高級編程.北京:北京希望電子出版社, 2001
【4】丁振凡.ASP應(yīng)用系統(tǒng)中用戶認(rèn)證設(shè)計.計算機(jī)時代,1999(8).
