關於近期開發中遇到的同一賬戶多人登入造成資料庫資料不一致的思考和解決(避開了資料庫存狀態的常用處理手段)
1.問題:近期開發了工單系統,開發結束專案上線測試過程中,發現同一賬戶多人同時登入如果不進行限制,該使用者的操作就不是唯一的,導致資料庫存放的資料出現了問題,專案整個就出問題了,經過本人再三思考,網上好多方案都是通過資料庫存一個登入狀態做處理,這樣的話又得對資料庫進行更改,很難受,那還有什麼其他的處理方案嗎?經思考,想出了以下處理方案:
1.採用servelet的application物件存放所有登入使用者的資訊,存放方式為sessionid-username儲存
2.在使用者登入時從application物件中取出所有的鍵,對應得值——即username與登入使用者名稱進行匹配,匹配成功說明已經登入,不讓該使用者登入,否則返給前臺登入成功的狀態。
3.前臺拿到資料後根據是否登入決定是否儲存sessionid及是否進行成功跳轉
4.使用者下線時從appication中移除該sessionid的鍵
2.具體程式碼:
2-1.index.jsp登入頁面:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";View Code%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> name:<input type=text id='name'/> <button>login in</button> <script type="text/javascript" src='js/jquery-1.8.3.js'></script> <script type="text/javascript" src='js/cookie.js'></script> <script type="text/javascript"> $('button').on('click',function(){ $.post('<%=path%>/loginservlet',{name:$('#name').val()},function(data){ console.dir(data); var json = eval("("+data+")"); if (!json.islogin&&json.flag==1){ console.dir('login is permited!'); setCookie("sessionId",json.sessionId,1); console.dir('set cookie success!it is'+json.sessionId); alert(json.sessionId); window.location.href='success.jsp'; }else{ alert('the account you are using is logined'); } }) }); </script> </body> </html>
2-2.homepage.jsp登入成功頁面:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>login success</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="sso login success page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <a href="javascript:void(0);" onclick='logout()'>logout</a><br/> ${name } ,welcome!!!<br> <script type="text/javascript" src='js/jquery-1.8.3.js'></script> <script type="text/javascript" src='js/cookie.js'></script> <script type="text/javascript"> function logout(){ var sessionId = getCookie("sessionId"); console.dir(sessionId); $.post('<%=path%>/logout',{sessionId:sessionId},function(data){ var json = eval("("+data+")"); console.dir(json); if (json.logout){ alert('logout success!'); window.location.href = "index.jsp"; }else{ alert('logout fail!'); } }); } </script> </body> </html>View Code
2-3.loginservlet.java後臺登入邏輯處理:
package com.java.gwb.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONObject; public class loginservlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String username = request.getParameter("name"); ServletContext application = request.getSession().getServletContext(); Enumeration<String> en = application.getAttributeNames(); boolean islogin = false; String sessionIds = request.getSession().getId(); JSONObject json = new JSONObject(); if (username.equals("gwb")||username.equals("ls")){ request.getSession().setAttribute("name", username); json.accumulate("flag", 1); while(en.hasMoreElements()){ String sessionId = en.nextElement(); if (application.getAttribute(sessionId).equals(username)){ islogin = true; } } json.accumulate("islogin", islogin); if (islogin){//如果已經登入,就不再存 json.accumulate("sessionId", ""); }else{ application.setAttribute(sessionIds, username); json.accumulate("sessionId", sessionIds); } }else{ json.accumulate("flag", 0); json.accumulate("sessionId", ""); } out.write(json.toString()); out.flush(); out.close(); } }View Code
2-4.logoutservlet.java後臺退出邏輯處理:
package com.java.gwb.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONObject; public class logoutservlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String sessionid = request.getParameter("sessionId"); ServletContext application = request.getSession().getServletContext(); boolean logout = false; JSONObject json = new JSONObject(); try{ application.removeAttribute(sessionid); logout = true; }catch (Exception e) { System.out.println("下線失敗"); } json.accumulate("logout", logout); out.write(json.toString()); out.flush(); out.close(); } }View Code
2-5.web.xml檔案:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <servlet> <description>This is the description of my J2EE component</description> <display-name>This is the display name of my J2EE component</display-name> <servlet-name>loginservlet</servlet-name> <servlet-class>com.java.gwb.servlet.loginservlet</servlet-class> </servlet> <servlet> <description>This is the description of my J2EE component</description> <display-name>This is the display name of my J2EE component</display-name> <servlet-name>logoutservlet</servlet-name> <servlet-class>com.java.gwb.servlet.logoutservlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginservlet</servlet-name> <url-pattern>/loginservlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>logoutservlet</servlet-name> <url-pattern>/logout</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>View Code
3.效果測試:使用不同的瀏覽器模擬多個使用者登入同一賬戶:
3-1.a瀏覽器登入成功後去b瀏覽器登入測試:
a登入成功圖
b登入被拒絕登入頁面
3-2.a瀏覽器登入使用者下線,b瀏覽器登入圖:
a瀏覽器登入使用者成功退出登入圖
b瀏覽器使用者在a退出後可以成功登入圖
4.存在的問題分析:
4-1.如果在訪問量比較大的情況下伺服器存了好多資料,每次登入要頻繁的進行伺服器操作,伺服器壓力是有些大
4-2.對於異常下線如直接關閉視窗或者直接關機,如果伺服器未重啟,使用者賬號就會一直登入不了,所以,這個問題有待解決。
5.優點:
5-1.雖然伺服器壓力大了,但是資料庫卻解放出來了,我們把資料庫的痛苦嫁接到了伺服器上了,哈哈。
5-2.可以在不改變資料庫表結構的情況下對使用者登入實現單點登陸的控制。