场景:项目中展示用户登录登出日志,遇到一些问题比如浏览器关闭未记录用户退出日志
项目技术:jsp+springmvc
后端需求:记录用户登录登出日志,要求浏览器关闭认为用户退出。
实现方法:
第一种.使用aop拦截用户登录登出请求,并记录日志 。
第二种.利用shiro监听器,在shiro认证成功后记录用户登录日志,退出时记录用户登出日志。
第三种.使用websocket,在websocket建立链接时记录用户登录日志,websocket断开链接时记录用户登出日志。
遇到的问题
存在问题一:浏览器并没有专门的处理浏览器关闭事件
存在问题二:根据浏览器关闭时间短,刷新请求接口相对时间长的特性来解决浏览器关闭事件
-
- var beginTime = 0;
- var differTime = 0;
- window.addEventListener('unload', logData, false);
- function logData() {
- differTime = new Date().getTime()-beginTime;
- if (differTime<=5) {
- var analyticsData={};
- navigator.sendBeacon(ctx_js+"/logout", analyticsData);
- }
- }
-
- window.addEventListener('beforeunload', logDataBefore, false);
- function logDataBefore(){
- beginTime = new Date().getTime();
- }
onbeforeunload,后onunload事件分析:
关闭页面时:先onbeforeunload,后onunload
刷新页面时:先onbeforeunload,后onunload,再onload
改版之前用的就是这种方法,但是发现时而正常,时而不正常(因为differTime这个对照时间的设定受多方面影响,网速等。 ),无奈不得不重新想其它方法。
最终方案:
思路:使用websocket实现,在websocket建立链接时记录用户登录日志,websocket断开链接时记录用户登出日志。
前端处理:
由于老项目前端登录采用的是form表单提交,因此在登录成功后第一次进入首页时,建立websocket
- //登录按钮提交
- document.getElementById(id).submit();
- //登录按钮提交后设置标志firstFlag, 此处为了使websocket在index页面只初始化一次,登录后先清除firstFlag
- var storage=window.localStorage;
- localStorage.removeItem("firstFlag");
在首页初始化的时候建立websocket连接
- /**
- * maogy 2017-7
- * 获取菜单定制中的数据
- */
- $(document).ready(function() {
- //在登录后第一次进入index页面时建立websocket连接
- var storage=window.localStorage;
- if(storage.getItem("firstFlag") == 'false' || storage.getItem("firstFlag")==null){
- initLoginWebSocket();
- }
- });
-
-
-
- /********wxy 2022/10/18 登录成功后建立websocket连接*********/
- function initLoginWebSocket(){
- //开启WebSocket
- var websocket = null;
- //变量loginWebSocketUrl从后端读取
- var websocketUrl = loginWebSocketUrl+"/GisqPlatformExplorer/ws/login.do";
- websocket = new WebSocket(websocketUrl);
- // 方法定义
- websocket.onopen = function(){
- //成功建立连接后设置firstFlag为true
- var storage=window.sessionStorage;
- storage.setItem("firstFlag", "true");
- };
- // 接收到服务端的消息
- websocket.onmessage = function(result) {
- //如果已经有弹窗,就不再弹窗
- };
- websocket.onerror = function(result){
- };
- websocket.onclose = function() {
- console.log("websocket close");
- };
- }
后端处理:
- package com.gisquest.platform.websocket.config;
- import com.gisquest.platform.websocket.handler.LoginWebSocketHandler;
- import com.gisquest.platform.websocket.interceptor.LoginOutInterceptor;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.socket.config.annotation.EnableWebSocket;
- import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
- import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
-
- /**
- * @author LiuDehuai
- * @fileName:WebSocketConfig
- * @Date:2021/11/5 10:56
- */
- @Configuration
- @EnableWebSocket
- public class WebSocketConfig implements WebSocketConfigurer {
- @Override
- public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
- webSocketHandlerRegistry
- //添加Handler
- .addHandler(loginWebSocketHandler(), "/ws/login.do")
- .addInterceptors(new LoginOutInterceptor())
- //允许跨域
- .setAllowedOrigins("*");
- }
-
-
-
- //登录时添加websocket连接
- @Bean
- public LoginWebSocketHandler loginWebSocketHandler() {
- return new LoginWebSocketHandler();
- }
- }
- package com.gisquest.platform.websocket.handler;
-
- import java.util.Map;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.util.CollectionUtils;
- import org.springframework.web.socket.CloseStatus;
- import org.springframework.web.socket.WebSocketMessage;
- import org.springframework.web.socket.WebSocketSession;
- import org.springframework.web.socket.handler.TextWebSocketHandler;
-
- import com.ctrip.framework.apollo.core.utils.StringUtils;
- import com.gisquest.platform.common.utils.CommonLogUtils;
- import com.gisquest.platform.common.utils.Constants;
- import com.gisquest.platform.websocket.util.WebSocketUtil;
-
- /**
- * 浏览器关闭使用WebSocket,记录用户退出日志
- * @author wxy
- * @fileName:LoginWebSocketHandler
- * @Date:2021/11/5 10:14
- */
- public class LoginWebSocketHandler extends TextWebSocketHandler {
- public static final Logger LOGGER = LoggerFactory.getLogger(LoginWebSocketHandler.class);
- /**
- * 用戶登錄时WebSocket连接成功建立
- * @param session
- * @throws Exception
- */
- @Override
- public void afterConnectionEstablished(WebSocketSession session) throws Exception {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("WebScoket会话已经建立,SessionId:{}", session.getId());
- }
- String username=StringUtils.EMPTY;
- String ip=StringUtils.EMPTY;
- String loginType=StringUtils.EMPTY;
- String sessionId=StringUtils.EMPTY;
- //获取属性
- Map<String, Object> attributes = session.getAttributes();
- if (!CollectionUtils.isEmpty(attributes)) {
- username = attributes.get("username") != null ? attributes.get("username").toString() : "";
- ip=attributes.get("loginIp").toString();
- loginType = Constants.LOGINMODEL;
- sessionId = session.getId();
- }
- CommonLogUtils.saveLog(username,ip,loginType,sessionId);
- //添加Session
- WebSocketUtil.put(session.getId(),session);
- //获取属性
- super.afterConnectionEstablished(session);
- }
-
- @Override
- public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("WebScoket收到前端消息,SessionId:{},消息:{}", session.getId(), message.getPayload());
- }
- System.out.println(session.getId());
- System.out.println(session);
- super.handleMessage(session, message);
- }
-
- @Override
- public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
- if (LOGGER.isWarnEnabled()) {
- LOGGER.warn("WebScoket连接发生错误,SessionId:{},错误:{}", session.getId(), exception.getMessage());
- }
- System.out.println(session.getId());
- System.out.println(session);
- super.handleTransportError(session, exception);
- }
-
- /**
- * 在任一方关闭WebSocket连接后或发生传输错误后调用
- * 关闭WebSocket连接
- * @param session
- * @param status
- * @throws Exception
- */
- @Override
- public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("WebScoket连接关闭,SessionId:{}", session.getId());
- }
- //记录用户关系浏览器和退出日志
- String username=StringUtils.EMPTY;
- String ip=StringUtils.EMPTY;
- String loginType=StringUtils.EMPTY;
- String sessionId=StringUtils.EMPTY;
- //获取属性
- Map<String, Object> attributes = session.getAttributes();
- if (!CollectionUtils.isEmpty(attributes)) {
- username = attributes.get("username") != null ? attributes.get("username").toString() : "";
- ip=attributes.get("loginIp").toString();
- loginType = Constants.LOGOUTMODEL;
- sessionId = session.getId();
- CommonLogUtils.saveLog(username,ip,loginType,sessionId);
- }
- //移除Session
- WebSocketUtil.remove(session.getId());
- super.afterConnectionClosed(session, status);
- }
- }