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

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