場景:java系統中用戶帳號登陸實現控制,實現用戶同時只能在一處登陸css
思路:java
分析: web
思路圖spring
//判斷當前帳號是否已經登陸 $.get('${ROOT}/platform/isLogon', function (r) { //已登陸,則增長提醒框,確認是否繼續登陸 if (r) { confirm("當前帳號已登陸,繼續登陸會使其餘登陸退出,確認繼續登陸嘛?", function () { // 登陸處理 isProcessing = true; $('#btnLogin').text('登陸中......').prop('disabled', 'true'); $.post ( '${ROOT}/platform/logon', params, function (rsp, textStatus, jqXHR) { //登陸請求的處理邏輯 } ); }, function () { //取消繼續登陸,則返回false return false; }); //未登陸則繼續登陸 } else { // 登陸處理 isProcessing = true; $('#btnLogin').text('登陸中......').prop('disabled', 'true'); $.post ( '${ROOT}/platform/logon', params, function (rsp, textStatus, jqXHR) { //登陸請求的處理邏輯 } ); } });
@RequestMapping(path = "/platform/isLogon", method = RequestMethod.GET) public boolean isLogon(HttpServletRequest req) throws Exception { IForm form = getFormWrapper(req); String loginId = form.get("userName"); SystemService service = GEOFF.SPRING_CONTEXT.getBean(SystemService.class); User user = service.isLogon(loginId); if (user != null && loginUserMap.size() >0){ String id = loginUserMap.get(user.getId().toString()); if (StringUtils.isNotBlank(id)){ lg.info("當前用戶:"+loginId+" 已登陸系統,增長登陸確認彈框..."); return true; } } lg.info("當前用戶:"+loginId+" 未登陸系統,正常登陸系統..."); return false; } @RequestMapping(path = "/platform/logon", method = RequestMethod.POST) public Object logon(HttpServletRequest req) throws Exception { /** ** **登陸時帳號,密碼,驗證碼的校驗邏輯 ** */ HttpSession session = req.getSession(); User sessionUser = (User) session.getAttribute("SESSION_USER"); if (StringUtils.isNotBlank(session.getId()) && sessionUser != null) { loginUserMap.put(sessionUser.getId().toString(), session.getId()); }else{ lg.error("進入保存用戶登陸信息到全局變量方法中,保存用戶登陸信息出錯!!!"); } Map<String, Object> rejson = new HashMap<>(); rejson.put("success", true); rejson.put("token", token); return JSON.toJSONString(rejson); }
import java.util.HashMap; import java.util.Map; public class Geoff { //================================================================= // 全局常量 //================================================================= /** * 存儲在線用戶,控制用戶帳號登陸 */ public static Map<String, String> loginUserMap = new HashMap<>(); }
package cn.geoff.framework.core; import cn.geoff.framework.core.controller.BaseController; import cn.geoff.framework.core.vo.InterfaceMessage; import cn.geoff.framework.core.vo.Message; import cn.geoff.framework.platform.vo.User; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import static cn.geoff.framework.core.FORP.loginUserMap; /** * @author: Geoff * @create: 2020-07-21 14:45 * @description: */ public class LoginInitInterceptor extends HandlerInterceptorAdapter { private static final Logger lg = LoggerFactory.getLogger(LoginInitInterceptor.class); /** * 攔截系統的請求,實現多帳號登陸控制 * @Author: Geoff * @date: 2020/7/22 11:13 * @param: request * @param response * @param handler * @return: boolean */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { lg.info("請求到達Interceptor攔截器,開始處理攔截邏輯..."); try { //獲取請求中的session和用戶對象 HttpSession session = request.getSession(); User sessionUser = (User) session.getAttribute(FORP.SESSION_USER); if (loginUserMap != null && loginUserMap.size() > 0 && sessionUser != null) { String sessionID = loginUserMap.get(sessionUser.getId().toString()); //進行非空校驗,如何請求session與全局信息中不一致,則清除緩存,並跳轉到登陸頁面 if (StringUtils.isNotBlank(sessionID)) { if (sessionID.equalsIgnoreCase(session.getId())) { //兩處sessionID一致則不作處理 lg.info("Interceptor攔截器攔截請求中的sessionID與全局登陸Map中一致!!!"); return true; }else{ //不一致則清除緩存 lg.info("Interceptor攔截器攔截請求中的sessionID與全局登陸Map中【不】一致,"+sessionUser.getUserName()+"強制退出系統!"); handleSessionTimeout(request,response); return false; } } } } catch (Exception e) { lg.error("Interceptor攔截器攔截請求出錯!!!"); } return false; } private void handleSessionTimeout(HttpServletRequest req, HttpServletResponse resp) throws Exception { lg.warn("Session已超時,重定向至登錄頁面:{}", req.getRequestURL()); lg.debug("Referer:{}", req.getHeader("Referer")); if (BaseController.isAjaxRequest(req)) { InterfaceMessage message = new InterfaceMessage(401, "請求失敗:無效的登陸信息!"); BaseController.writeJsonResponse(message, resp); } else { Message message = new Message("您的帳號已在其它地方登陸,如非本人操做,請及時修改密碼!"); message.setType(3); req.setAttribute("msg", message); req.setAttribute("actionURL", "/"); req.getRequestDispatcher("/WEB-INF/jsp/common/message.jsp").forward(req, resp); } } }
<!--多帳號登陸控制攔截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--排查登陸接口攔截--> <mvc:exclude-mapping path="/"/> <mvc:exclude-mapping path="/platform/isLogon"/> <mvc:exclude-mapping path="/platform/logon"/> <mvc:exclude-mapping path="/platform/error/**"/> <!--排除靜態資源請求--> <mvc:exclude-mapping path="/favicon.ico"/> <mvc:exclude-mapping path="/js/**"/> <mvc:exclude-mapping path="/css/**"/> <mvc:exclude-mapping path="/fonts/**"/> <mvc:exclude-mapping path="/image/**"/> <mvc:exclude-mapping path="/images/**"/> <mvc:exclude-mapping path="/app/**"/> <mvc:exclude-mapping path="/disk-file/**"/> <mvc:exclude-mapping path="/sound/**"/> <mvc:exclude-mapping path="/geoff/**"/> <bean class="cn.geoff.framework.core.LoginInitInterceptor"/> </mvc:interceptor>
package cn.geoff.framework.core; import cn.geoff.framework.core.util.Redis; import cn.geoff.framework.platform.vo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import static cn.geoff.framework.core.GEOFF.loginUserMap; /** * session監聽器,監聽session的建立與銷燬 * @author: Geoff * @create: 2020-07-21 09:42 * @description: */ public class SessionListener implements HttpSessionListener { private static final Logger lg = LoggerFactory.getLogger(SessionListener.class); /** * 實現session接口,監聽session建立的方法 * @Author: Geoff * @date: 2020/7/21 9:43 * @param: httpSessionEvent * @return: void */ @Override public void sessionCreated(HttpSessionEvent event) { lg.info("session監聽器初始化加載......"); } /** * 實現session接口,監聽session的銷燬方法 * @Author: Geoff * @date: 2020/7/21 9:44 * @param: httpSessionEvent * @return: void */ @Override public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); //獲取當前session中用戶 User sessionUser = (User)session.getAttribute(GEOFF.SESSION_USER); //若是用戶不爲空,銷燬對應的session if (sessionUser != null){ try { //清除session中對用的用戶信息 session.removeAttribute(GEOFF.SESSION_USER); //獲取當前的token String workToken = (String)session.getAttribute("workToken"); //清除Redis中用戶的登陸信息 Redis.delete(workToken,null); //清除整個session信息 session.invalidate(); }catch (Exception e){ lg.error(sessionUser.getUserName()+":用戶對應session超時後,銷燬session信息出錯!"); }finally { //清除已保存的用戶登陸信息 loginUserMap.remove(sessionUser.getId().toString()); } } } }
<!--多帳號控制session監聽器--> <listener> <listener-class>cn.geoff.framework.core.SessionListener</listener-class> </listener>
/** * 退出系統 * @param id 用戶ID * @param req 請求參數 */ @RequestMapping("/platform/logout/{id}") public ModelAndView logout(@PathVariable Long id, HttpServletRequest req) { lg.info("退出系統:userId[{}]", id); // 清除用戶權限緩衝 User user = getSessionUser(req); //清除session中用戶信息,並將session初始 req.getSession().removeAttribute(GEOFF.SESSION_USER); req.getSession().invalidate(); //清除已保存的用戶登陸信息 loginUserMap.remove(user.getId().toString()); // 返回首頁 return new ModelAndView("redirect:/"); }