Java 多用戶登錄限制的實(shí)現(xiàn)方法
最近比較空閑沒有項(xiàng)目做,于是乎捋了捋平時(shí)工作會(huì)遇到的一些常見問題,首先想到了多用戶登錄限制問題,下面就對(duì)此問題做一點(diǎn)思考講解。
相關(guān)閱讀:
Java Web開發(fā)防止多用戶重復(fù)登錄的完美解決方案
1、設(shè)計(jì)場(chǎng)景
1)同一時(shí)刻不允許某個(gè)用戶多地登錄
2)用戶已在A處登錄,現(xiàn)在從B處登錄是允許的,但會(huì)把A處擠掉(考慮到用戶在A處登錄后因某些情況跑到了B處,但還想繼續(xù)之前的工作,所以需要登錄系統(tǒng))
3)B處擠掉A后,A再做其它操作的時(shí)候系統(tǒng)會(huì)給出提示,該用戶在別處登錄,如不是本人操作可能密碼泄漏,請(qǐng)修改密碼。
2、思路導(dǎo)圖
每個(gè)用戶登錄的時(shí)候,通常我們會(huì)將用戶信息存入session,以便用戶進(jìn)行操作的時(shí)候系統(tǒng)方便得到用戶的基本信息。但這個(gè)session具有私有性,只對(duì)當(dāng)前用戶可見(如果同意用戶在不同瀏覽器登錄會(huì)得到不同的session,這也是為什么可以多用戶登錄的根源所在)。那么接著問題就來(lái)了,某個(gè)用戶登錄的時(shí)候如何能知道自己是否在線,相信聰明的你已經(jīng)想到,這還不好半,把在線的用戶信息存儲(chǔ)在一個(gè)公共的地方問題不就迎刃而解了么,網(wǎng)上一查,解決方案無(wú)出其右,大致為以下兩種
1)數(shù)據(jù)庫(kù)中標(biāo)識(shí)在線用戶
2)存儲(chǔ)到application中
經(jīng)過重重考慮,我們會(huì)發(fā)現(xiàn)方案一需要解決許多棘手的問題(用戶異常退出未來(lái)得及修改狀態(tài),頻繁訪問數(shù)據(jù)庫(kù)影響性能等),這對(duì)于一個(gè)要求完美的你來(lái)說(shuō)顯然是不合時(shí)宜的,于是我們采用了方案二,將在線用戶信息保存到application中,具體設(shè)計(jì)如下。
1)登錄流程圖
2)被擠掉后操作流程圖
3、代碼
1)登錄方法
@RequestMapping(value = "/login", method = RequestMethod.POST) public String login(String userName, String password, RedirectAttributes redirectAttributes, HttpServletRequest request) { //判斷用戶是否已經(jīng)在線及處理(已在線則剔除) String loginLimite = limiteLogin.loginLimite(request, userName); //判斷用戶名、密碼是否正確 String result = userService.login(userName, password); if (result.equals("success")) { request.getSession().setAttribute("now_user", userService.findByUserName(userName)); //創(chuàng)建token及驗(yàn)證 String jwtToken = tokenService.createUserAuthToken(userService.findByUserName(userName));//生成token System.out.println(jwtToken); UserAuthenticationToken authToken = tokenService.retrieveUserAuthToken(jwtToken);//token解析 System.out.println(authToken.isAuthenticated()); System.out.println("id = " + UserAuthenticationToken.getCurrentToken().getUserUuid()); //用戶掉線,登錄后重定向到保存的鏈接 Object url = request.getSession().getAttribute("redirect_link"); if (url != null) { request.getSession().removeAttribute("redirect_link"); return "redirect:" + url.toString(); } return "index"; } redirectAttributes.addFlashAttribute("message", result); return "redirect:/other/toLogin"; }
2)登錄判斷是否已經(jīng)在線
@Service @Transactional public class LimiteLogin { private static Logger log = Logger.getLogger(SessionListener.class); private static Map<String, String> loginUserMap = new HashMap<>();//存儲(chǔ)在線用戶 private static Map<String, String> loginOutTime = new HashMap<>();//存儲(chǔ)剔除用戶時(shí)間 @Autowired private UserService userService; public String loginLimite(HttpServletRequest request, String userName) { User user = userService.findByUserName(userName); String sessionId = request.getSession().getId(); for (String key : loginUserMap.keySet()) { //用戶已在另一處登錄 if (key.equals(user.getUserName()) && !loginUserMap.containsValue(sessionId)) { log.info("用戶:" + user.getUserName() + ",于" + DateUtil.dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "被剔除!"); loginOutTime.put(user.getUserName(), DateUtil.dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss")); loginUserMap.remove(user.getUserName()); break; } } loginUserMap.put(user.getUserName(), sessionId); request.getSession().getServletContext().setAttribute("loginUserMap", loginUserMap); request.getSession().getServletContext().setAttribute("loginOutTime", loginOutTime); return "success"; } }
3)登錄攔截器(未登錄跳轉(zhuǎn)登錄頁(yè))
public class LoginInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User user = (User) session.getAttribute("now_user"); if (session.getAttribute("now_user") == null) { response.sendRedirect(request.getContextPath() + "/other/toLogin"); return false; } //多用戶登錄限制判斷,并給出提示信息 boolean isLogin = false; if (user != null) { Map<String, String> loginUserMap = (Map<String, String>) session.getServletContext().getAttribute("loginUserMap"); String sessionId = session.getId(); for (String key : loginUserMap.keySet()) { //用戶已在另一處登錄 if (key.equals(user.getUserName()) && !loginUserMap.containsValue(sessionId)) { isLogin = true; break; } } } if (isLogin) { Map<String, String> loginOutTime = (Map<String, String>) session.getServletContext().getAttribute("loginOutTime"); session.setAttribute("mess", "用戶:" + user.getUserName() + ",于 " + loginOutTime.get(user.getUserName()) + " 已在別處登錄!"); loginOutTime.remove(user.getUserName()); session.getServletContext().setAttribute("loginUserMap", loginOutTime); response.sendRedirect(request.getContextPath() + "/other/toLogin"); return false; } return super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { super.afterCompletion(request, response, handler, ex); } }
4)在session銷毀的時(shí)候,把loginUserMap中保存的鍵值對(duì)清除
public classSessionListener implements HttpSessionListener { private static Logger log = Logger.getLogger(SessionListener.class); @Override public void sessionCreated(HttpSessionEvent event) { } @Override public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); String sessionId = session.getId(); //在session銷毀的時(shí)候,把loginUserMap中保存的鍵值對(duì)清除 User user = (User) session.getAttribute("now_user"); if (user != null) { Map<String, String> loginUserMap = (Map<String, String>) event.getSession().getServletContext().getAttribute("loginUserMap"); if(loginUserMap.get(user.getUserName()).equals(sessionId)){ log.info("clean user from application : " + user.getUserName()); loginUserMap.remove(user.getUserName()); event.getSession().getServletContext().setAttribute("loginUserMap", loginUserMap); } } } }
5)web.xml
<!-- session listener 多用戶登錄限制,退出清除session信息的同時(shí)清除application中存放用戶登錄信息--> <listener> <listener-class>com.service.limitelogin.SessionListener</listener-class> </listener>
6)頁(yè)面代碼(用于給出提示的同時(shí),清除被擠掉用戶的session信息,否則提示信息會(huì)一直顯示)
<script type="text/javascript"> $(document).ready(function () { var message='${mess}'; if (message != "") { $.ajax({ type: 'GET', async: false, cache: false, url: '/other/clearUserSession', dataType: '', data: {}, success: function (data) { } }); $('#mess').html(message); } }); </script>
7)清除擠掉用戶session代碼
/** * 多用戶登錄限制,清除session信息(登錄信息、提示信息) * * @param request * @return */ @ResponseBody @RequestMapping(value = "/clearUserSession") public String clearUserSession(HttpServletRequest request) { HttpSession httpSession = request.getSession(); //httpSession.invalidate(); httpSession.removeAttribute("now_user"); httpSession.removeAttribute("mess"); return "success"; }
到此開發(fā)工作完成
4、運(yùn)行結(jié)果
以上所述是小編給大家介紹的Java 多用戶登錄限制的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Java中對(duì)象快速?gòu)?fù)制的幾種方式詳解
這篇文章主要介紹了Java中對(duì)象快速?gòu)?fù)制的幾種方式詳解,對(duì)象的克隆是指創(chuàng)建一個(gè)新的對(duì)象,且新的對(duì)象的狀態(tài)與原始對(duì)象的狀態(tài)相同,當(dāng)對(duì)克隆的新對(duì)象進(jìn)行修改時(shí),不會(huì)影響原始對(duì)象的狀態(tài),需要的朋友可以參考下2023-08-08Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort,本文直接給出實(shí)例代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-06-06MyBatis實(shí)現(xiàn)MySQL批量插入的示例代碼
本文主要介紹了MyBatis實(shí)現(xiàn)MySQL批量插入的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05java生成excel并導(dǎo)出到對(duì)應(yīng)位置的方式
這篇文章主要介紹了java生成excel并導(dǎo)出到對(duì)應(yīng)位置的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Java實(shí)現(xiàn)發(fā)送手機(jī)短信語(yǔ)音驗(yàn)證功能代碼實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)發(fā)送手機(jī)短信語(yǔ)音驗(yàn)證功能代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09SpringBoot @Validated注解實(shí)現(xiàn)參數(shù)分組校驗(yàn)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于SpringBoot @Validated注解實(shí)現(xiàn)參數(shù)分組校驗(yàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09