多用戶登錄限制


代碼下載:login_limit

參考:java web項目防止多用戶重復登錄解決方案

最近閑來沒事,但就這么閑着心里老不踏實,總得找點事干干啊,大好時光不能白白浪費啊,於是乎捋了捋平時工作會遇到的一些常見問題,首先想到了多用戶登錄限制問題,下面就對此問題做一點思考講解。

1、設計場景

  1)同一時刻不允許某個用戶多地登錄

  2)用戶已在A處登錄,現在從B處登錄是允許的,但會把A處擠掉(考慮到用戶在A處登錄后因某些情況跑到了B處,但還想繼續之前的工作,所以需要登錄系統)

  3)B處擠掉A后,A再做其它操作的時候系統會給出提示,該用戶在別處登錄,如不是本人操作可能密碼泄漏,請修改密碼。

2、思路導圖

  每個用戶登錄的時候,通常我們會將用戶信息存入session,以便用戶進行操作的時候系統方便得到用戶的基本信息。但這個session具有私有性,只對當前用戶可見(如果同意用戶在不同瀏覽器登錄會得到不同的session,這也是為什么可以多用戶登錄的根源所在)。那么接着問題就來了,某個用戶登錄的時候如何能知道自己是否在線,相信聰明的你已經想到,這還不好半,把在線的用戶信息存儲在一個公共的地方問題不就迎刃而解了么,網上一查,解決方案無出其右,大致為以下兩種

  1)數據庫中標識在線用戶

  2)存儲到application中

  經過重重考慮,我們會發現方案一需要解決許多棘手的問題(用戶異常退出未來得及修改狀態,頻繁訪問數據庫影響性能等),這對於一個要求完美的你來說顯然是不合時宜的,於是我們采用了方案二,將在線用戶信息保存到application中,具體設計如下。

  1)登錄流程圖

  

  2)被擠掉后操作流程圖

  

3、代碼

  1)登錄方法

  

@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));

        //用戶掉線,登錄后重定向到保存的鏈接 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)登錄判斷是否已經在線

@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)登錄攔截器(未登錄跳轉登錄頁)

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中保存的鍵值對清除

public class SessionListener 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

<!-- session listener 多用戶登錄限制,退出清除session信息的同時清除application中存放用戶登錄信息-->
  <listener>
    <listener-class>com.service.limitelogin.SessionListener</listener-class>
  </listener>

  6)頁面代碼(用於給出提示的同時,清除被擠掉用戶的session信息,否則提示信息會一直顯示) 

<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";
    }

到此開發工作完成

4、運行結果

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM