瀏覽代碼

增加监听浏览器关闭退出记录功能

xusl 1 年之前
父節點
當前提交
fd0df9b17a

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/config/WebConfig.java

@@ -36,7 +36,7 @@ public class WebConfig implements WebMvcConfigurer {
     public void addInterceptors(InterceptorRegistry registry){
         // 刷新token拦截器注册
         registry.addInterceptor(new TokenStatusInterceptor()).addPathPatterns("/**")
-                .excludePathPatterns("/sysParameterController/getUseSendMail","/refreshToken","/error","/getMailCode","/getVerifyCode","/login","/index.html","/user/login","/css/**","/images/**","/js/**","/fonts/**");
+                .excludePathPatterns("/sysParameterController/getUseSendMail","/refreshToken","/error","/getMailCode","/getVerifyCode","/sysUserController/establishHeart","/login","/index.html","/user/login","/css/**","/images/**","/js/**","/fonts/**");
     }
     /**
      * 初始加载

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/config/WebSecurityConfig.java

@@ -107,7 +107,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .and().authorizeRequests()
 //                .antMatchers("/user/login","/captchaImage").permitAll()
                 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
-                .antMatchers("/getVerifyCode/**","/getMailCode/**").permitAll()
+                .antMatchers("/getVerifyCode/**","/getMailCode/**","/sysUserController/establishHeart/**").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and().headers().cacheControl();

+ 16 - 0
backend/src/main/java/com/jiayue/ssi/constant/CacheConstants.java

@@ -1,7 +1,11 @@
 package com.jiayue.ssi.constant;
 
+import com.jiayue.ssi.dto.ActiveUserDto;
 import com.jiayue.ssi.entity.SysBlacklist;
+import org.springframework.security.core.Authentication;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -60,6 +64,10 @@ public class CacheConstants {
      */
     public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
     /**
+     * 服务端心跳
+     */
+    public static final String HEART_KEY = "heart:";
+    /**
      * 登录token存储,map<用户名,token:授权时间>
      */
     public static Map<String,String> LOGIN_TOKEN_MAP = new ConcurrentHashMap<String,String>();
@@ -77,4 +85,12 @@ public class CacheConstants {
      * 用户名对应明文密码缓存
      */
     public static ConcurrentMap<String, String> usernamePasswordMap = new ConcurrentHashMap<>();
+    /**
+     * 当前服务端访问的IP集合<IP,用户名>
+     */
+    public static ConcurrentMap<String, String> IP_USER_MAP = new ConcurrentHashMap<>();
+    /**
+     * 用于用户心跳
+     */
+    public static Map<String, ActiveUserDto> ACTIVE_USER_MAP = new ConcurrentHashMap<String,ActiveUserDto>();
 }

+ 30 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysUserController.java

@@ -19,16 +19,24 @@ import com.jiayue.ssi.constant.ApproveConstants;
 import com.jiayue.ssi.constant.CacheConstants;
 import com.jiayue.ssi.constant.CustomException;
 import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.dto.ActiveUserDto;
 import com.jiayue.ssi.entity.*;
 import com.jiayue.ssi.service.*;
 import com.jiayue.ssi.service.impl.SysPermissionService;
 import com.jiayue.ssi.util.*;
+import io.jsonwebtoken.Claims;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -56,6 +64,8 @@ public class SysUserController {
     SysRoleService sysRoleService;
     @Autowired
     SysPolicyService sysPolicyService;
+    @Autowired
+    JwtTokenUtil jwtTokenUtil;
 
     /**
      * 获取用户分页信息
@@ -810,4 +820,24 @@ public class SysUserController {
             throw new CustomException("获取用户异常", e);
         }
     }
+
+    /**
+     * 建立心跳
+     */
+    @GetMapping(value = "/establishHeart")
+    public ResponseVO establishHeart(HttpServletRequest request, HttpServletResponse response) throws CustomException {
+        try {
+            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+            SysUser sysUser = (SysUser)authentication.getPrincipal();
+            ActiveUserDto activeUserDto = new ActiveUserDto();
+            activeUserDto.setAuthentication(authentication);
+            activeUserDto.setHttpServletRequest(request);
+            CacheConstants.ACTIVE_USER_MAP.put(sysUser.getUsername(),activeUserDto);
+            LocalCache.set(CacheConstants.HEART_KEY+sysUser.getUsername(),"在线",4000);
+            System.out.println("存心跳:"+DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
+            return ResponseVO.success();
+        } catch (Exception e) {
+            throw new CustomException("建立心跳异常", e);
+        }
+    }
 }

+ 18 - 0
backend/src/main/java/com/jiayue/ssi/dto/ActiveUserDto.java

@@ -0,0 +1,18 @@
+package com.jiayue.ssi.dto;
+
+import lombok.Data;
+import org.springframework.security.core.Authentication;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+* 应用Map<String, HttpServletRequest> ACTIVE_USER的DTO
+*
+* @author xsl
+* @since 2023/08/17
+*/
+@Data
+public class ActiveUserDto {
+    private HttpServletRequest httpServletRequest;
+    private Authentication authentication;
+}

+ 2 - 2
backend/src/main/java/com/jiayue/ssi/filter/InterfaceLimitFilter.java

@@ -82,8 +82,8 @@ public class InterfaceLimitFilter extends OncePerRequestFilter {
                 return;
             }
         }
-        if (!InterfaceLimitUtil.checkInterface(request, 1000, 20)) {
-            log.info("接口拦截:{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), 10,1000, remoteIp);
+        if (!InterfaceLimitUtil.checkInterface(request, 1000, 50)) {
+            log.info("接口拦截:{} 请求超过限制频率【{}次/{}ms】,IP为{}", request.getRequestURI(), 50,1000, remoteIp);
             // 锁定ip黑名单
             SysBlacklist sysBlacklist = new SysBlacklist();
             sysBlacklist.setIp(remoteIp);

+ 31 - 3
backend/src/main/java/com/jiayue/ssi/filter/JwtAuthenticationTokenFilter.java

@@ -2,6 +2,8 @@ package com.jiayue.ssi.filter;
 
 import java.io.IOException;
 import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -66,7 +68,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws
             ServletException, IOException {
-        SecurityContextHolder.getContext().getAuthentication();
+//        SecurityContextHolder.getContext().getAuthentication();
         String token = request.getHeader("Authorization");
         if (!StringUtils.isEmpty(token)) {
             String username = "";
@@ -195,23 +197,49 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
                     // 设置标题
                     operLog.setTitle("连接超时");
                     // 操作描述
-                    operLog.setOperdesc("连接超时");
+                    operLog.setOperdesc("超出非活动");
                     // 审计类型
                     operLog.setAuditType(AuditType.SYS.ordinal());
                     // 保存数据库
                     OperateLogFactory.recordOper(operLog);
 
                     LocalCache.remove(CacheConstants.REACTIVE_KEY + token);
+
+                    SysUser sysUser = (SysUser)SecurityContextHolder.getContext().getAuthentication();
+                    // 设置用户离线状态
+                    sysUser.setOnlineStatus("1");
+                    sysUserService.updateUser(sysUser);
+                    CacheConstants.LOGIN_TOKEN_MAP.remove(sysUser.getUsername());
+                    LoginConstants.sessionMap.remove(sysUser.getUsername());
+                    Iterator<Map.Entry<String, String>> countMap = CacheConstants.IP_USER_MAP.entrySet().iterator();
+                    while (countMap.hasNext()) {
+                        Map.Entry<String, String> entry1 = countMap.next();
+                        String cacheusername = entry1.getValue();
+                        if (cacheusername.equals(sysUser.getUsername())){
+                            countMap.remove();
+                        }
+                    }
+
                     // 超出配置设定值则退出
                     ResponseInfo.doResponse(response, "超出非活动时长退出!", 406);
                     return;
                 }
                 else{
-                    LocalCache.set(CacheConstants.REACTIVE_KEY + token,System.currentTimeMillis(),1000*60*60);
+                    if (!request.getRequestURI().equals("/sysUserController/establishHeart")){
+                        LocalCache.set(CacheConstants.REACTIVE_KEY + token,System.currentTimeMillis(),1000*60*60);
+                    }
                 }
             }
         } else {
             if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {
+                String currentIp = IPUtils.getIpAddr();
+                String ip = CacheConstants.IP_USER_MAP.get(currentIp);
+                if (ip!=null && !"".equals(ip)){
+                    // 判断当前访问的ip是否已经存在,如果存在则不让任何用户访问
+                    ResponseInfo.doResponse(response, "您的客户端IP有用户正在使用,不能登录系统!", 405);
+                    return;
+                }
+
                 // 判断并发会话数是否满足
                 SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
                 if (LoginConstants.sessionMap.size()+1>sysPolicy.getBfhhs()){

+ 12 - 0
backend/src/main/java/com/jiayue/ssi/filter/VerifySmFilter.java

@@ -2,10 +2,12 @@ package com.jiayue.ssi.filter;
 
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
+import com.jiayue.ssi.constant.CacheConstants;
 import com.jiayue.ssi.constant.Constants;
 import com.jiayue.ssi.constant.SecretKeyConstants;
 import com.jiayue.ssi.factory.LoginFactory;
 import com.jiayue.ssi.servlet.ParameterRequestWrapper;
+import com.jiayue.ssi.util.IPUtils;
 import com.jiayue.ssi.util.ResponseInfo;
 import com.jiayue.ssi.util.SM2CryptUtils;
 import lombok.RequiredArgsConstructor;
@@ -36,6 +38,16 @@ public class VerifySmFilter extends OncePerRequestFilter {
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
         try {
+            if (request.getRequestURI().equals("/getVerifyCode") || request.getRequestURI().equals("/getMailCode") || request.getRequestURI().equals(defaultFilterProcessUrl)){
+                String currentIp = IPUtils.getIpAddr();
+                String ip = CacheConstants.IP_USER_MAP.get(currentIp);
+                if (ip!=null && !"".equals(ip)){
+                    // 判断当前访问的ip是否已经存在,如果存在则不让任何用户访问
+                    ResponseInfo.doResponse(response, "您的客户端IP有用户正在使用,不能登录系统!", 405);
+                    return;
+                }
+            }
+
             Map<String, Object> stringToMap = new HashMap(16);
             ParameterRequestWrapper initWrapper = new ParameterRequestWrapper(request);
             // 不是登录操作

+ 17 - 3
backend/src/main/java/com/jiayue/ssi/handler/CustomAuthenticationSuccessHandler.java

@@ -2,7 +2,9 @@ package com.jiayue.ssi.handler;
 
 import java.io.IOException;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -78,10 +80,12 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
         LoginFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
 
         String token = jwtTokenUtil.generateToken(sysUser);
+        // 访问时间
+        Long vtime = System.currentTimeMillis();
         // token加入缓存,用于并发会话处理
         UserVisitInfoDto userVisitInfoDto = new UserVisitInfoDto();
         userVisitInfoDto.setUsername(username);
-        userVisitInfoDto.setVtime(System.currentTimeMillis());
+        userVisitInfoDto.setVtime(vtime);
         userVisitInfoDto.setIp(ip);
         userVisitInfoDto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
         // 获取客户端操作系统
@@ -94,6 +98,15 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
 
         // 将token存储内存中,便于重复登录比对
         if (CacheConstants.LOGIN_TOKEN_MAP.get(username)!=null){
+            // 将之前用户登录过的ip删除掉
+            Iterator<Map.Entry<String, String>> countMap = CacheConstants.IP_USER_MAP.entrySet().iterator();
+            while (countMap.hasNext()) {
+                Map.Entry<String, String> entry = countMap.next();
+                String cacheusername = entry.getValue();
+                if (cacheusername.equals(username)){
+                    countMap.remove();
+                }
+            }
             // 之前有用户登录过,本次将上次用户踢出下线。并通知管理员
             String noticeWay = "";
             SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
@@ -134,8 +147,9 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
         }
         CacheConstants.LOGIN_TOKEN_MAP.put(username,token);
         // 登录成功后将用户本次操作时间存入缓存,为了判断非活动退出时应用,60分钟自动失效
-        LocalCache.set(CacheConstants.REACTIVE_KEY + token,System.currentTimeMillis(),1000*60*60);
-
+        LocalCache.set(CacheConstants.REACTIVE_KEY + token,vtime,1000*60*60);
+        // 将登录成功的用户ip加入缓存中
+        CacheConstants.IP_USER_MAP.put(ip,username);
 
         String obj = JSONUtil.toJsonStr(ResponseVO.success(token));
         // token加密处理

+ 20 - 10
backend/src/main/java/com/jiayue/ssi/handler/CustomLogoutSuccessHandler.java

@@ -20,6 +20,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
 
 /**
  * 自定义退出处理类 返回成功
@@ -39,16 +41,24 @@ public class CustomLogoutSuccessHandler implements org.springframework.security.
     @Override
     public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
             throws IOException, ServletException {
-        SysUser sysUser = (SysUser)authentication.getPrincipal();
-        // 设置用户离线状态
-        sysUser.setOnlineStatus("1");
-        sysUserService.updateUser(sysUser);
-
-        // 记录用户退出日志
-        LoginFactory.recordLogininfor(sysUser.getUsername(), Constants.LOGIN_SUCCESS, "退出成功");
-        // 将token存储内存中,便于重复登录比对
-        CacheConstants.LOGIN_TOKEN_MAP.remove(sysUser.getUsername());
-        LoginConstants.sessionMap.remove(sysUser.getUsername());
+//        SysUser sysUser = (SysUser)authentication.getPrincipal();
+//        // 设置用户离线状态
+//        sysUser.setOnlineStatus("1");
+//        sysUserService.updateUser(sysUser);
+//
+//        // 记录用户退出日志
+//        LoginFactory.recordLogininfor(sysUser.getUsername(), Constants.LOGIN_SUCCESS, "退出成功");
+//        // 将token存储内存中,便于重复登录比对
+//        CacheConstants.LOGIN_TOKEN_MAP.remove(sysUser.getUsername());
+//        LoginConstants.sessionMap.remove(sysUser.getUsername());
+//        Iterator<Map.Entry<String, String>> countMap = CacheConstants.IP_USER_MAP.entrySet().iterator();
+//        while (countMap.hasNext()) {
+//            Map.Entry<String, String> entry = countMap.next();
+//            String cacheusername = entry.getValue();
+//            if (cacheusername.equals(sysUser.getUsername())){
+//                countMap.remove();
+//            }
+//        }
         String obj = JSONUtil.toJsonStr(ResponseVO.success("退出成功"));
         // token加密处理
         String encrypt = SM2CryptUtils.encrypt(obj, SecretKeyConstants.CLIENT_PUBLIC_KEY);

+ 28 - 19
backend/src/main/java/com/jiayue/ssi/job/AutoCheckSessionMap.java

@@ -1,6 +1,7 @@
 package com.jiayue.ssi.job;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.jiayue.ssi.constant.CacheConstants;
 import com.jiayue.ssi.constant.Constants;
 import com.jiayue.ssi.constant.LoginConstants;
 import com.jiayue.ssi.dto.UserVisitInfoDto;
@@ -37,24 +38,32 @@ public class AutoCheckSessionMap {
      */
     @Scheduled(cron = "0 0/1 * * * ?")
     public void auditBak() throws Exception{
-        SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
-        // 非活动登出时间(分钟)
-        int inactiveLogout = sysPolicy.getInactiveLogout();
-
-        long iil = inactiveLogout * 1000 * 60;
-
-        Iterator<Map.Entry<String, UserVisitInfoDto>> entries = LoginConstants.sessionMap.entrySet().iterator();
-        while (entries.hasNext()) {
-            Map.Entry<String, UserVisitInfoDto> entry = entries.next();
-            // 判断会话连接里失效的用户
-            UserVisitInfoDto userVisitInfoDto = entry.getValue();
-            Long userTime = userVisitInfoDto.getVtime();
-            if (System.currentTimeMillis()> userTime + iil){
-                // 踢用户
-                LoginConstants.sessionMap.remove(entry.getKey());
-                // 记录用户退出日志
-                LoginFactory.autoRecordLogininfor(userVisitInfoDto, Constants.LOGIN_SUCCESS, "超时登出");
-            }
-        }
+//        SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
+//        // 非活动登出时间(分钟)
+//        int inactiveLogout = sysPolicy.getInactiveLogout();
+//
+//        long iil = inactiveLogout * 1000 * 60;
+//
+//        Iterator<Map.Entry<String, UserVisitInfoDto>> entries = LoginConstants.sessionMap.entrySet().iterator();
+//        while (entries.hasNext()) {
+//            Map.Entry<String, UserVisitInfoDto> entry = entries.next();
+//            // 判断会话连接里失效的用户
+//            UserVisitInfoDto userVisitInfoDto = entry.getValue();
+//            Long userTime = userVisitInfoDto.getVtime();
+//            if (System.currentTimeMillis()> userTime + iil){
+//                // 踢用户
+//                LoginConstants.sessionMap.remove(entry.getKey());
+//                Iterator<Map.Entry<String, String>> countMap = CacheConstants.IP_USER_MAP.entrySet().iterator();
+//                while (countMap.hasNext()) {
+//                    Map.Entry<String, String> entry1 = countMap.next();
+//                    String cacheusername = entry1.getValue();
+//                    if (cacheusername.equals(userVisitInfoDto.getUsername())){
+//                        countMap.remove();
+//                    }
+//                }
+//                // 记录用户退出日志
+//                LoginFactory.autoRecordLogininfor(userVisitInfoDto, Constants.LOGIN_SUCCESS, "超时登出");
+//            }
+//        }
     }
 }

+ 93 - 0
backend/src/main/java/com/jiayue/ssi/job/AutoScanHeartUser.java

@@ -0,0 +1,93 @@
+package com.jiayue.ssi.job;
+
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.constant.Constants;
+import com.jiayue.ssi.constant.LoginConstants;
+import com.jiayue.ssi.dto.ActiveUserDto;
+import com.jiayue.ssi.entity.SysLogininfor;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.service.SysLogininforService;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.util.*;
+import eu.bitwalker.useragentutils.UserAgent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+* 自动扫描心跳用户
+*
+* @author xsl
+* @since 2023/04/06
+*/
+@Service
+@EnableScheduling
+@Slf4j
+public class AutoScanHeartUser {
+    @Autowired
+    SysUserService sysUserService;
+
+    /**
+     * 每2秒扫描一次
+     */
+    @Scheduled(fixedRate=2000)
+    public void autoScanHeartUser() throws Exception{
+        Iterator<Map.Entry<String, ActiveUserDto>> activeUserIterator = CacheConstants.ACTIVE_USER_MAP.entrySet().iterator();
+        while (activeUserIterator.hasNext()) {
+            Map.Entry<String,ActiveUserDto> entry = activeUserIterator.next();
+            String username = entry.getKey();
+            ActiveUserDto activeUserDto = entry.getValue();
+            SysUser sysUser = (SysUser)activeUserDto.getAuthentication().getPrincipal();
+
+            if (LocalCache.get(CacheConstants.HEART_KEY+username)==null){
+                // 设置用户离线状态
+                sysUser.setOnlineStatus("1");
+                sysUserService.updateUser(sysUser);
+                // 记录用户退出日志
+                HttpServletRequest request = activeUserDto.getHttpServletRequest();
+                final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
+                final String ip = IPUtils.getIpAddr(request);
+                String address = AddressUtils.getRealAddressByIP(ip);
+                // 获取客户端操作系统
+                String os = userAgent.getOperatingSystem().getName();
+                // 获取客户端浏览器
+                String browser = userAgent.getBrowser().getName();
+                // 封装对象
+                SysLogininfor logininfor = new SysLogininfor();
+                logininfor.setUserName(sysUser.getUsername());
+                logininfor.setIpaddr(ip);
+                logininfor.setLoginLocation(address);
+                logininfor.setBrowser(browser);
+                logininfor.setOs(os);
+                logininfor.setMsg("退出成功");
+                logininfor.setLoginTime(new Date());
+                logininfor.setCreateBy(sysUser.getUsername());
+                // 日志状态
+                logininfor.setStatus(Constants.SUCCESS);
+                // 插入数据
+                SpringUtils.getBean(SysLogininforService.class).insertLogininfor(logininfor);
+
+                // 将token存储内存中,便于重复登录比对
+                CacheConstants.LOGIN_TOKEN_MAP.remove(sysUser.getUsername());
+                LoginConstants.sessionMap.remove(sysUser.getUsername());
+                Iterator<Map.Entry<String, String>> ipUserMap = CacheConstants.IP_USER_MAP.entrySet().iterator();
+                while (ipUserMap.hasNext()) {
+                    Map.Entry<String, String> entry1 = ipUserMap.next();
+                    String cacheusername = entry1.getValue();
+                    if (cacheusername.equals(entry.getKey())){
+                        ipUserMap.remove();
+                    }
+                }
+                activeUserIterator.remove();
+            }
+            System.out.println("activeUserMap数量:"+CacheConstants.ACTIVE_USER_MAP.size());
+        }
+    }
+}

+ 22 - 7
backend/src/main/java/com/jiayue/ssi/util/InterfaceLimitUtil.java

@@ -1,9 +1,12 @@
 package com.jiayue.ssi.util;
 
+import com.jiayue.ssi.constant.CacheConstants;
 import net.jodah.expiringmap.ExpirationPolicy;
 import net.jodah.expiringmap.ExpiringMap;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
@@ -14,7 +17,8 @@ import java.util.concurrent.TimeUnit;
 * @since 2023/02/24
 */
 public class InterfaceLimitUtil {
-    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> interfaceLimitMap = new ConcurrentHashMap<>();
+//    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> interfaceLimitMap = new ConcurrentHashMap<>();
+    private static ConcurrentHashMap<String, Integer> interfaceLimitMap = new ConcurrentHashMap<>();
 
     /**
      * 接口检测*
@@ -24,16 +28,27 @@ public class InterfaceLimitUtil {
      * @return boolean true:允许通过 false:不通过
      */
     public synchronized static boolean checkInterface(HttpServletRequest request,long time,int value){
-        ExpiringMap<String, Integer> uc = interfaceLimitMap.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
-        Integer uCount = uc.getOrDefault(IPUtils.getIpAddr(request), 0);
+        Integer uCount = 0;
+        if (LocalCache.get(request.getRequestURI())==null){
+            // 首次访问
+            LocalCache.set(request.getRequestURI(),request.getRequestURI(),time);
+        }
+        else{
+            uCount = interfaceLimitMap.get(request.getRequestURI());
+        }
         if (uCount >= value) { // 超过次数,不执行目标方法
+            Iterator<Map.Entry<String, Integer>> countMap = interfaceLimitMap.entrySet().iterator();
+            while (countMap.hasNext()) {
+                Map.Entry<String, Integer> entry = countMap.next();
+                String requestURI = entry.getKey();
+                if (requestURI.equals(request.getRequestURI())){
+                    countMap.remove();
+                }
+            }
             return false;
-        } else if (uCount == 0) { // 第一次请求时,设置有效时间
-            uc.put(IPUtils.getIpAddr(request), uCount + 1, ExpirationPolicy.CREATED, time, TimeUnit.MILLISECONDS);
         } else { // 未超过次数, 记录加一
-            uc.put(IPUtils.getIpAddr(request), uCount + 1);
+            interfaceLimitMap.put(request.getRequestURI(),uCount + 1);
         }
-        interfaceLimitMap.put(request.getRequestURI(), uc);
         return true;
     }
 }

+ 5 - 0
backend/src/main/java/com/jiayue/ssi/util/LocalCache.java

@@ -69,6 +69,11 @@ public class LocalCache {
         TIMED_CACHE.clear();
     }
 
+    public static long test() {
+        return TIMED_CACHE.timeout();
+
+    }
+
     public static class Constants {
         public static final String DICT_LIST_PREFIX = "dict:list:";
         public static final String DICT_ONE_PREFIX = "dict:one:";

+ 51 - 48
ui/src/App.vue

@@ -16,56 +16,59 @@ export default {
       return false;
     };
 
-    window.addEventListener("beforeunload", e => {
-      this.beforeunloadHandler(e)
-      let userAgent = navigator.userAgent
-      let isOpera = userAgent.indexOf("Opera") > -1;
-      if (isOpera) { //判断是否Opera浏览器
-        return "Opera"
-      }
-      if (userAgent.indexOf("Firefox") > -1) {
-        this.unloadHandler();
-      }
-      if (userAgent.indexOf("MSIE") > -1) {
-        this.unloadHandler();
-      }
-    });
-    window.addEventListener("unload", async e => {
-      this.unloadHandler(e)
-    });
+    // window.addEventListener("beforeunload", e => {
+    //   this.beforeunloadHandler(e)
+    //   let userAgent = navigator.userAgent
+    //   let isOpera = userAgent.indexOf("Opera") > -1;
+    //   if (isOpera) { //判断是否Opera浏览器
+    //     return "Opera"
+    //   }
+    //   else{
+    //     this.unloadHandler();
+    //   }
+    //   // if (userAgent.indexOf("Firefox") > -1) {
+    //   //   this.unloadHandler();
+    //   // }
+    //   // if (userAgent.indexOf("MSIE") > -1) {
+    //   //   this.unloadHandler();
+    //   // }
+    // });
+    // window.addEventListener("unload", async e => {
+    //   this.unloadHandler(e)
+    // });
   },
   methods: {
-    beforeunloadHandler(e) {
-      this._beforeUnload_time = new Date().getTime();
-    },
-    unloadHandler(e) {
-      this._gap_time = new Date().getTime() - this._beforeUnload_time;
-      //判断是窗口关闭还是刷新
-      localStorage.setItem('time', this._gap_time)
-      if (this._gap_time <= 5) {
-        // // 发送设置同步 (退出登陆的api)
-        // this.$store.dispatch('LogOut').then(() => {
-        //   location.href = '/index';
-        // })
-
-
-        this.$axios.post(
-          '/logout', {}
-        ).then((res) => {
-          this.$message.success(res.data)
-          removeToken()
-          //注销返回自己的登录页
-          this.$router.push(`/login?redirect=${this.$route.fullPath}`)
-        })
-      } else {
-      }
-    },
+    // beforeunloadHandler(e) {
+    //   this._beforeUnload_time = new Date().getTime();
+    // },
+    // unloadHandler(e) {
+    //   this._gap_time = new Date().getTime() - this._beforeUnload_time;
+    //   //判断是窗口关闭还是刷新
+    //   localStorage.setItem('time', this._gap_time)
+    //   if (this._gap_time <= 5) {
+    //     // // 发送设置同步 (退出登陆的api)
+    //     // this.$store.dispatch('LogOut').then(() => {
+    //     //   location.href = '/index';
+    //     // })
+    //
+    //
+    //     this.$axios.post(
+    //       '/logout', {}
+    //     ).then((res) => {
+    //       this.$message.success(res.data)
+    //       removeToken()
+    //       //注销返回自己的登录页
+    //       this.$router.push(`/login?redirect=${this.$route.fullPath}`)
+    //     })
+    //   } else {
+    //   }
+    // },
   },
-  destroyed() {
-    window.removeEventListener("beforeunload", e =>
-      this.beforeunloadHandler(e)
-    );
-    window.removeEventListener("unload", e => this.unloadHandler(e));
-  }
+  // destroyed() {
+  //   window.removeEventListener("beforeunload", e =>
+  //     this.beforeunloadHandler(e)
+  //   );
+  //   window.removeEventListener("unload", e => this.unloadHandler(e));
+  // }
 }
 </script>

+ 23 - 0
ui/src/layout/components/AppMain.vue

@@ -22,7 +22,30 @@ export default {
     key() {
       return this.$route.path
     }
+  },
+
+  data() {
+    return {
+      timer: null,
+      intervalId:null
+    }
+  },
+  created() {
+    this.intervalId = setInterval(this.myFunction, 1000); // 设置初始定时器
+  },
+  destroyed() {
+    clearInterval(this.intervalId)
+  },
+  methods: {
+    myFunction() {
+      // 在这里执行你的代码逻辑
+      this.$axios.get('/sysUserController/establishHeart').then((res) => {
+      })
+      //clearInterval(intervalId); // 清除之前的定时器
+      //this.intervalId = setInterval(myFunction, 1000); // 设置新的定时器
+    }
   }
+
 }
 </script>
 

+ 1 - 0
ui/src/utils/request.js

@@ -139,6 +139,7 @@ service.interceptors.response.use(
           // 返回 401 清除token信息并跳转到登录页面
           removeToken()
           resetRouter()
+          router.push('/login')
           Message({
             message: error.response.data,
             type: 'error',