那么我們應(yīng)該怎樣去實(shí)現(xiàn)同一用戶只能有一個(gè)在線這樣的一個(gè)小功能呢?
有人可能就會(huì)這樣設(shè)想了:"這不是很簡(jiǎn)單嗎?只要在數(shù)據(jù)庫(kù)中用一個(gè)字段來(lái)標(biāo)記用戶的狀態(tài)就行了,比如如果用戶登陸了就將狀態(tài)設(shè)為1,退出了就將這個(gè)用戶的狀態(tài)設(shè)為0,OK,搞定。"
但是,實(shí)際上是不是這樣呢?其實(shí)不全是。為什么這樣說(shuō)呢?其實(shí)如果你的想法跟上面那樣或相似的話,應(yīng)該說(shuō)是犯了一個(gè)比較嚴(yán)重的錯(cuò)誤。我還是舉個(gè)例 子來(lái)說(shuō)明吧。現(xiàn)在絕大多數(shù)的網(wǎng)站中都有登陸和退出兩項(xiàng)功能吧?好了,上面的設(shè)想僅僅是針對(duì)這兩項(xiàng)功能來(lái)說(shuō)使用。但是你有沒(méi)有想過(guò)?假如現(xiàn)在有一個(gè)用戶正常 登陸上了,但是這回情況有點(diǎn)特殊了,這個(gè)用戶登陸上但是這個(gè)用戶就偏偏不點(diǎn)退出,然后就走了或者離開(kāi)了或者忙別的事情去了,反正這個(gè)用戶登陸上就不管別的 了,他就掛在那里。這種情況是允許發(fā)生了,而且也是比較常見(jiàn)的一種情況。那如果是這種情況,上面的那種設(shè)想你還認(rèn)為是正確的嗎?那就不正確了!對(duì) session有過(guò)一點(diǎn)了解的人員應(yīng)該都知道,在java中session的默認(rèn)的銷毀時(shí)間是大于或等于30分鐘,如果你對(duì)session的生命周期不做 任何配置的話,按照上面的設(shè)想,那么只要用戶登陸上之后,這時(shí)該用戶的狀態(tài)設(shè)置為1,在大于30分鐘的時(shí)間內(nèi)如果該用戶沒(méi)有向服務(wù)器端發(fā)起任何請(qǐng)求的話, 那么這個(gè)session就會(huì)被銷毀掉,注意了,這時(shí)session生命周期結(jié)束以后自動(dòng)銷毀的,并不是用戶點(diǎn)退出按鈕來(lái)銷毀的,那這樣就不能觸發(fā)用戶退出 事件,那這個(gè)用戶的狀態(tài)你就沒(méi)法改變了,也就是說(shuō),如果按照上面的設(shè)想,你想想,如果遇到這樣的情況,那這個(gè)用戶的狀態(tài)就一直都是1了,那這個(gè)用戶以后再 想登陸就再也登陸不上了。很明顯,這樣是不對(duì)的。
那應(yīng)該怎樣來(lái)解決這個(gè)問(wèn)題呢?大家看到我這篇文章的標(biāo)題就應(yīng)該知道了的吧。可以使用java的監(jiān)聽(tīng)器來(lái)解決這個(gè)問(wèn)題。在編程的開(kāi)始你應(yīng)該有這樣一個(gè)了解:
當(dāng)用戶通過(guò)網(wǎng)絡(luò)來(lái)訪問(wèn)一個(gè)網(wǎng)站的時(shí)候,如果是首次訪問(wèn),那么在這個(gè)網(wǎng)站的服務(wù)器端都會(huì)創(chuàng)建一個(gè)session來(lái)保存一些屬于這個(gè)用戶的信息。在創(chuàng) 建session的時(shí)候其實(shí)是會(huì)觸發(fā)一個(gè)sessionCreated事件的,同樣的,當(dāng)用戶正常退出或者是用戶登陸了不退出并當(dāng)session生命周期 結(jié)束的時(shí)候,就會(huì)觸發(fā)一個(gè)sessionDestroyed事件。這兩個(gè)事件我們可以通過(guò)HttpSessionListener監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)到并可以把 它捕捉。那這樣問(wèn)題就好解決了。
我話說(shuō)的也有點(diǎn)多了,朋友們不要介意哈。好了,下面來(lái)看一下代碼
注:為了演示簡(jiǎn)單,我就不對(duì)用戶做封裝了,也不使用數(shù)據(jù)庫(kù)了,同樣的我也不添加任何的SSH框架支持了,我知道你們都懂的。不懂的可以給我留言。 在這里我就直接用servlet來(lái)模擬了。我直接將用戶登陸后的信息保存到一個(gè)ServletContext對(duì)象中。順便我也簡(jiǎn)單說(shuō)一下 ServletContext吧,怕有人對(duì)ServletContext不了解的。ServletContext對(duì)象是在你項(xiàng)目第一次啟動(dòng)服務(wù)器的時(shí)候被 創(chuàng)建的,這個(gè)對(duì)象是只被創(chuàng)建一次,是唯一的,你可以用ServletContextListener這個(gè)監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)的到。
下面來(lái)看實(shí)現(xiàn)吧:
我先做jsp吧,這里我需要三個(gè)jsp頁(yè)面:一個(gè)登陸login.jsp,一個(gè)首頁(yè)home.jsp,一個(gè)錯(cuò)誤提示error.jsp。我盡量將jsp寫(xiě)的簡(jiǎn)單些,下面來(lái)開(kāi)代碼,我就不多解釋了,一看就懂的
login.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用戶登錄</title>
</head>
<body>
[align=center]<br/><br/>
<form action="/SingleOnline/servlet/LoginServlet" method="post">
<table>
<tr>
<td>用戶昵稱:</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>用戶密碼:</td>
<td><input type="password" name="userpssw" size="21" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value=" 登陸 " /></td>
</tr>
</table>
</form>
[/align]
</body>
</html>
home.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用戶主頁(yè)</title>
</head>
<body>
<!-- ${user}是EL表達(dá)式,如果用戶登陸成功就將用戶名字顯示出來(lái) -->
用戶 ${user} 登陸成功!<BR/>
[url=/SingleOnline/servlet/LogoutServlet]安全退出[/url]
</body>
</html>
error.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>error page</title>
<script type="text/javascript">
function warn(){
alert('您已經(jīng)登陸在線,不能重復(fù)登陸!');
}
</script>
</head>
<body onload="warn()">
您已經(jīng)登陸在線,不能重復(fù)登陸! <br>
[url=/SingleOnline/home.jsp]返回主頁(yè)[/url]
</body>
</html>
下面來(lái)看一下登陸的servlet
LoginServlet:
package com.servlet;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LoginServlet() {
super();
}
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
doPost(request, response);
}
@SuppressWarnings("unchecked")
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
String uname = request.getParameter("username");
@SuppressWarnings("unused")
String upssw = request.getParameter("userpssw");
String url = "/SingleOnline/home.jsp";
try {
uname = new String(uname.getBytes("ISO-8859-1"));
} catch (Exception e) {e.printStackTrace();}
ServletContext context = this.getServletContext(); //獲取ServletContext
ArrayList<String> users = (ArrayList<String>)context.getAttribute("users"); //獲取用戶列表,第一次獲取時(shí)候?yàn)榭?br /> if(users == null){ //第一個(gè)用戶登錄時(shí)候:
users = new ArrayList<String>();
users.add(uname);
context.setAttribute("users", users); //將第一個(gè)用戶的名字保存到ServletContext對(duì)象中
}else{ //非第一個(gè)用戶登錄:
for(int i=0;i<users.size();i++){
String username = (String)users.get(i);
聰明女人與笨女人~! if(username.equals(uname)){ //如果該用戶已經(jīng)登錄,請(qǐng)求error.jsp不讓其再登錄
url = "/SingleOnline/error.jsp";
break;
}
}
users.add(uname); //如果該用戶沒(méi)經(jīng)登錄,就將該用戶的名字保存到ServletContext對(duì)象中
}
request.getSession().setAttribute("user", uname); //保存一下該用戶信息以備后用
response.sendRedirect(url);
}
}
接下來(lái)是用戶點(diǎn)擊安全退出需要的servlet:
LogoutServlet:
package com.servlet;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LogoutServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LogoutServlet() {
super();
}
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
doPost(request, response);
}
@SuppressWarnings("unchecked")
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
String user = (String)request.getSession().getAttribute("user"); //獲取用戶信息
ArrayList<String> users = (ArrayList<String>)this.getServletContext().getAttribute("users"); // 獲取用戶列表
for(int i=0;i<users.size();i++){
String username = (String)users.get(i);
if(username.equals(user)){
users.remove(i); //將這個(gè)用戶從ServletContext對(duì)象中移除
break;
}
}
request.getSession().invalidate(); //將session設(shè)置成無(wú)效
response.sendRedirect("/SingleOnline/login.jsp");
}
}
最后就是監(jiān)聽(tīng)器了,寫(xiě)監(jiān)聽(tīng)器類也是很簡(jiǎn)單的,只要實(shí)現(xiàn)相應(yīng)的監(jiān)聽(tīng)器接口并實(shí)現(xiàn)未實(shí)現(xiàn)的方法就行了。下面我寫(xiě)一個(gè)SessionListener,它實(shí)現(xiàn)了HttpSessionListener接口:
package com.listener;
import java.util.ArrayList;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class SessionListener implements HttpSessionListener {
//session創(chuàng)建時(shí),該方法被調(diào)用
//為了給朋友們看的清楚一些,我在兩個(gè)方法中都打印了一下信息
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("Session被創(chuàng)建!");
}
//session銷毀時(shí)候被調(diào)用(1.用戶安全退出;2.session生命周期結(jié)束)
@SuppressWarnings("unchecked")
public void sessionDestroyed(HttpSessionEvent arg0) {
HttpSession session = arg0.getSession(); //獲取session
String user = (String)session.getAttribute("user"); //獲取用戶信息
ArrayList<String> users = (ArrayList<String>)session.getServletContext().getAttribute("users"); //獲取用戶列表
for(int i=0;i<users.size();i++){
String username = (String)users.get(i);
if(username.equals(user)){
users.remove(i); //將這個(gè)用戶從ServletContext對(duì)象中移除
break;
}
}
session.invalidate(); //將session設(shè)置成無(wú)效
System.out.println("一個(gè)Session被銷毀了!");
}
}
posted on 2011-11-01 15:14
墻頭草 閱讀(608)
評(píng)論(0) 編輯 收藏