Java Web防止使用者重複(同一使用者同時)登入實現方式

老頭學程式設計發表於2020-11-04

相信我們在開發web工程的時候,都會遇到同一使用者可以同時登入進系統的問題。對於開發的時候我們可能不是太關注這個問題,但對客戶來說,他們肯定覺得是不可以的。那麼我們在日常開發的過程中有幾種解決方案。

1、如果我們的專案使用的是 Spring 框架的話,Spring security 就可以實現防止使用者重複登入的問題。當然有些web工程可能對於許可權的控制要求沒有那麼嚴格,單單為了一個登入功能去實現Spring security 就有些大材小用了。我們還有其他的解決方案。

2、資料庫層面設計,可以為使用者表加登入狀態欄位判斷,登入成功後,修改狀態為1,退出後修改狀態為0。但是此種方案貌似也有缺陷,如果某個使用者登入進系統之後,沒有正常退出那就坑了,狀態如何修改?肯定相應的要做個管理員清退功能,將該使用者強制下線。此外每次登陸都去操作一下資料庫是不是有些多餘了呢?沒關係,我們可以利用更簡單的方式來實現。

3、我們可以利用 Servlet三大域物件 request、session、application(ServletContext)中的ServletContext,它是一個全域性的儲存資訊的空間,伺服器啟動後就被建立,伺服器停止後才銷燬。

request,一個使用者可有多個;session,一個使用者一個;而servletContext,所有使用者共用一個。所以,為了節省空間,提高效率,ServletContext中,要放必須的、重要的、所有使用者需要共享的執行緒又是安全的一些資訊。

具體方案:將使用者的登入資訊儲存在application裡, 然後利用session監聽器HttpSessionListener監聽每一個登入使用者的登入情況。

1、在自己專案的登入功能中新增如下程式碼

ServletContext application = session.getServletContext();
@SuppressWarnings("unchecked")
Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
if (loginMap == null) {
    loginMap = new HashMap<String, Object>();
}
for (String key : loginMap.keySet()) {
    if (login.getUser_id().equals(key)) {
	    if (session.getId().equals(loginMap.get(key))) {
	        return redirectUrl.append("login.action?mesg=")
		    .append(URLEncoder.encode(username + "在同一地點重複登入", "UTF-8")).toString();
	    } else {
                return redirectUrl.append("login.action?mesg=")
		    .append(URLEncoder.encode(username + "異地已登入,請先退出登入", "UTF-8")).toString();
	    }
    }
}
loginMap.put(login.getUser_id(),session.getId());
application.setAttribute("loginMap", loginMap);
// 將使用者儲存在session當中
session.setAttribute("user", user);
// session 銷燬時間
session.setMaxInactiveInterval(10*60);

2、編寫一個實現類 SessionListener,重寫HttpSessionListener

/**
 * session監聽器
 * 
 * @author Fyq
 *
 */
public class SessionListener implements HttpSessionListener {
 
	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		
	}
 
	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		HttpSession session = httpSessionEvent.getSession();
		SysUserModel user = (SysUserModel) session.getAttribute("user");
		if (user != null) {
			ServletContext application = session.getServletContext();
			@SuppressWarnings("unchecked")
			Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
			loginMap.remove(user.getUser_id());
			application.setAttribute("loginMap", loginMap);
			session.removeAttribute("user");
		}
	}
 
}

sessionCreated session建立的時候使用,這裡如果我們需用做統計線上人數等功能時,可以自己去實現自己的邏輯程式碼了

sessionDestroyed session銷燬的時候用,session 銷燬時呼叫邏輯去清除登入資訊。

上述session銷燬時間可以按照自己實際需求去定。session監聽器的作用是為了監聽那些非正常退出系統的使用者,清除登入資訊用的。

3、退出系統的方法和監聽器中銷燬session方法中的邏輯大體相同,根據自身專案的實際情況去定

@RequestMapping(value = "/logout.action", method = RequestMethod.POST)
	public void loginOut(HttpServletRequest request, HttpServletResponse response) {
		HttpSession session = request.getSession();
		String user_id = request.getParameter("user_id");
		if (session != null) {
			try {
				session.removeAttribute("user");
				String failSN = user_id + Contants.JOIN_STR;
				if (session.getAttribute(failSN) != null) {
					session.removeAttribute(failSN);
				}
				ServletContext application = session.getServletContext();
				@SuppressWarnings("unchecked")
				Map<String, Object> loginMap = (Map<String, Object>) application.getAttribute("loginMap");
				loginMap.remove(user_id);
				application.setAttribute("loginMap", loginMap);
			} catch (Exception e) {
				e.printStackTrace();
				AJAXUtil.handleSuccess(response, false);
			}
		}
		AJAXUtil.handleSuccess(response, true);
	}

4、記得需要在web.xml中新增監聽器

<listener>
    <listener-class>com.csdn.framework.SessionListener</listener-class>  
</listener>

相關文章