最近比較空閑沒有項目做,于是乎捋了捋平時工作會遇到的一些常見問題,首先想到了多用戶登錄限制問題,下面就對此問題做一點思考講解。
相關閱讀:
1、設計場景
1)同一時刻不允許某個用戶多地登錄
2)用戶已在A處登錄,現在從B處登錄是允許的,但會把A處擠掉(考慮到用戶在A處登錄后因某些情況跑到了B處,但還想繼續之前的工作,所以需要登錄系統)
3)B處擠掉A后,A再做其它操作的時候系統會給出提示,該用戶在別處登錄,如不是本人操作可能密碼泄漏,請修改密碼。
2、思路導圖
每個用戶登錄的時候,通常我們會將用戶信息存入session,以便用戶進行操作的時候系統方便得到用戶的基本信息。但這個session具有私有性,只對當前用戶可見(如果同意用戶在不同瀏覽器登錄會得到不同的session,這也是為什么可以多用戶登錄的根源所在)。那么接著問題就來了,某個用戶登錄的時候如何能知道自己是否在線,相信聰明的你已經想到,這還不好半,把在線的用戶信息存儲在一個公共的地方問題不就迎刃而解了么,網上一查,解決方案無出其右,大致為以下兩種
1)數據庫中標識在線用戶
2)存儲到application中
經過重重考慮,我們會發現方案一需要解決許多棘手的問題(用戶異常退出未來得及修改狀態,頻繁訪問數據庫影響性能等),這對于一個要求完美的你來說顯然是不合時宜的,于是我們采用了方案二,將在線用戶信息保存到application中,具體設計如下。
1)登錄流程圖
2)被擠掉后操作流程圖
3、代碼
1)登錄方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@RequestMapping (value = "/login" , method = RequestMethod.POST) public String login(String userName, String password, RedirectAttributes redirectAttributes, HttpServletRequest request) { //判斷用戶是否已經在線及處理(已在線則剔除) String loginLimite = limiteLogin.loginLimite(request, userName); //判斷用戶名、密碼是否正確 String result = userService.login(userName, password); if (result.equals( "success" )) { request.getSession().setAttribute( "now_user" , userService.findByUserName(userName)); //創建token及驗證 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)登錄判斷是否已經在線
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Service @Transactional public class LimiteLogin { private static Logger log = Logger.getLogger(SessionListener. class ); private static Map<String, String> loginUserMap = new HashMap<>(); //存儲在線用戶 private static Map<String, String> loginOutTime = new HashMap<>(); //存儲剔除用戶時間 @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)登錄攔截器(未登錄跳轉登錄頁)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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銷毀的時候,把loginUserMap中保存的鍵值對清除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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銷毀的時候,把loginUserMap中保存的鍵值對清除 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
1
2
3
4
|
<!-- session listener 多用戶登錄限制,退出清除session信息的同時清除application中存放用戶登錄信息--> <listener> <listener- class >com.service.limitelogin.SessionListener</listener- class > </listener> |
6)頁面代碼(用于給出提示的同時,清除被擠掉用戶的session信息,否則提示信息會一直顯示)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<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代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** * 多用戶登錄限制,清除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" ; } |
到此開發工作完成
4、運行結果
以上所述是小編給大家介紹的Java 多用戶登錄限制的實現方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!