From 84333544560aec1f3be03e9870631936d039a7a1 Mon Sep 17 00:00:00 2001
From: xiangpei <xiangpei@timesnew.cn>
Date: 星期四, 11 七月 2024 09:47:37 +0800
Subject: [PATCH] 增加session过滤器,阻止同一个用户同时登录

---
 src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java |   10 ++++
 src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java    |    8 ++++
 src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java                                      |   13 ++++++
 src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java               |    7 +++
 src/main/java/com/ycl/jxkg/config/spring/security/SessionFilter.java                    |   61 ++++++++++++++++++++++++++++++
 src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java         |   10 ++++
 6 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java b/src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java
index a5ead92..58df9d5 100644
--- a/src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java
+++ b/src/main/java/com/ycl/jxkg/config/spring/security/RestAuthenticationSuccessHandler.java
@@ -1,10 +1,12 @@
 package com.ycl.jxkg.config.spring.security;
 
 import com.ycl.jxkg.base.SystemCode;
+import com.ycl.jxkg.constants.CaffeineConstant;
 import com.ycl.jxkg.domain.entity.UserEventLog;
 import com.ycl.jxkg.enums.general.YesOrNoEnum;
 import com.ycl.jxkg.event.UserEvent;
 import com.ycl.jxkg.service.UserService;
+import com.ycl.jxkg.utils.CaffeineUtil;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
@@ -12,6 +14,7 @@
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -31,6 +34,7 @@
 
     private final ApplicationEventPublisher eventPublisher;
     private final UserService userService;
+    private final CaffeineUtil caffeineUtil;
 
     /**
      * Instantiates a new Rest authentication success handler.
@@ -39,9 +43,10 @@
      * @param userService    the user service
      */
     @Autowired
-    public RestAuthenticationSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService) {
+    public RestAuthenticationSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService, CaffeineUtil caffeineUtil) {
         this.eventPublisher = eventPublisher;
         this.userService = userService;
+        this.caffeineUtil = caffeineUtil;
     }
 
     @Override
@@ -49,6 +54,9 @@
         Object object = authentication.getPrincipal();
         if (null != object) {
             User springUser = (User) object;
+            // 鐧诲綍涔嬪悗淇濆瓨鎴栨洿鏂� 鐢ㄦ埛鍚嶄笌session鐨勫叧绯�
+            String sessionId = request.getSession().getId();
+            caffeineUtil.put(CaffeineConstant.AUTH, springUser.getUsername(), sessionId);
             com.ycl.jxkg.domain.entity.User user = userService.getUserByUserName(springUser.getUsername());
             if (null != user) {
                 // 瀵嗙爜杩囨湡杩斿洖寮哄埗淇敼瀵嗙爜鏍囪瘑
diff --git a/src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java b/src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java
index 21f7f13..5407567 100644
--- a/src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java
+++ b/src/main/java/com/ycl/jxkg/config/spring/security/RestLoginAuthenticationFilter.java
@@ -2,8 +2,10 @@
 
 import com.ycl.jxkg.config.property.CookieConfig;
 
+import com.ycl.jxkg.utils.CaffeineUtil;
 import com.ycl.jxkg.utils.JsonUtil;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
@@ -16,6 +18,7 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Enumeration;
 
 
 /**
@@ -25,7 +28,11 @@
  * @date 2021/12/25 9:45
  */
 public class RestLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
+
     private final org.slf4j.Logger logger = LoggerFactory.getLogger(RestLoginAuthenticationFilter.class);
+
+    @Autowired
+    private CaffeineUtil caffeineUtil;
 
     /**
      * Instantiates a new Rest login authentication filter.
@@ -40,6 +47,7 @@
         try (InputStream is = request.getInputStream()) {
             AuthenticationBean authenticationBean = JsonUtil.toJsonObject(is, AuthenticationBean.class);
             request.setAttribute(TokenBasedRememberMeServices.DEFAULT_PARAMETER, authenticationBean.isRemember());
+
             authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.getUserName(), authenticationBean.getPassword());
         } catch (IOException e) {
             logger.error(e.getMessage(), e);
diff --git a/src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java b/src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java
index 9a35774..5517844 100644
--- a/src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java
+++ b/src/main/java/com/ycl/jxkg/config/spring/security/RestLogoutSuccessHandler.java
@@ -1,13 +1,16 @@
 package com.ycl.jxkg.config.spring.security;
 
 import com.ycl.jxkg.base.SystemCode;
+import com.ycl.jxkg.constants.CaffeineConstant;
 import com.ycl.jxkg.domain.entity.User;
 import com.ycl.jxkg.domain.entity.UserEventLog;
 import com.ycl.jxkg.event.UserEvent;
 import com.ycl.jxkg.service.UserService;
+import com.ycl.jxkg.utils.CaffeineUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
 import org.springframework.stereotype.Component;
 
@@ -27,6 +30,7 @@
 
     private final ApplicationEventPublisher eventPublisher;
     private final UserService userService;
+    private final CaffeineUtil caffeineUtil;
 
     /**
      * Instantiates a new Rest logout success handler.
@@ -35,15 +39,19 @@
      * @param userService    the user service
      */
     @Autowired
-    public RestLogoutSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService) {
+    public RestLogoutSuccessHandler(ApplicationEventPublisher eventPublisher, UserService userService, CaffeineUtil caffeineUtil) {
         this.eventPublisher = eventPublisher;
         this.userService = userService;
+        this.caffeineUtil = caffeineUtil;
     }
 
     @Override
     public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
         org.springframework.security.core.userdetails.User springUser = (org.springframework.security.core.userdetails.User) authentication.getPrincipal();
         if (null != springUser) {
+            // 娓呴櫎鐢ㄦ埛鍚嶅拰sessionId涔嬮棿鐨勭粦瀹�
+            caffeineUtil.remove(CaffeineConstant.AUTH, springUser.getUsername());
+            SecurityContextHolder.clearContext();
             User user = userService.getUserByUserName(springUser.getUsername());
             UserEventLog userEventLog = new UserEventLog(user.getId(), user.getUserName(), user.getRealName(), new Date());
             userEventLog.setContent(user.getUserName() + " 鐧诲嚭浜嗗涔嬫�濆紑婧愯�冭瘯绯荤粺");
diff --git a/src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java b/src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java
index 596abfb..3bf787b 100644
--- a/src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java
+++ b/src/main/java/com/ycl/jxkg/config/spring/security/SecurityConfigurer.java
@@ -67,6 +67,12 @@
             this.restAccessDeniedHandler = restAccessDeniedHandler;
         }
 
+        @Bean
+        public SessionFilter sessionFilter() throws Exception {
+            SessionFilter jwtTokenFilter = new SessionFilter(authenticationManagerBean());
+            return jwtTokenFilter;
+        }
+
         /**
          * @param http http
          * @throws Exception exception
@@ -95,6 +101,7 @@
                     .and().rememberMe().key(CookieConfig.getName()).tokenValiditySeconds(CookieConfig.getInterval()).userDetailsService(formDetailsService)
                     .and().csrf().disable()
                     .cors();
+            http.addFilter(sessionFilter());
         }
 
 
diff --git a/src/main/java/com/ycl/jxkg/config/spring/security/SessionFilter.java b/src/main/java/com/ycl/jxkg/config/spring/security/SessionFilter.java
new file mode 100644
index 0000000..c458a10
--- /dev/null
+++ b/src/main/java/com/ycl/jxkg/config/spring/security/SessionFilter.java
@@ -0,0 +1,61 @@
+package com.ycl.jxkg.config.spring.security;
+
+
+import com.ycl.jxkg.base.SystemCode;
+import com.ycl.jxkg.constants.CaffeineConstant;
+import com.ycl.jxkg.utils.CaffeineUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextImpl;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Objects;
+
+/**
+ * @author 29443
+ * @date 2022/4/4
+ */
+@Slf4j
+public class SessionFilter extends BasicAuthenticationFilter {
+
+    @Autowired
+    private CaffeineUtil caffeineUtil;
+
+    public SessionFilter(AuthenticationManager authenticationManager) {
+        super(authenticationManager);
+    }
+
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+        // 妫�鏌ヨ姹備腑鏄惁鏈塻ecurity璁剧疆鐨勮璇佷俊鎭�傛病鏈夌殑璇濇湰Filter涓嶅仛澶勭悊
+        SecurityContextImpl securityContext = (SecurityContextImpl) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
+        if (Objects.isNull(securityContext)) {
+            chain.doFilter(request, response);
+            return;
+        }
+        // 鏈夌殑璇濋獙璇�
+        Authentication authentication = securityContext.getAuthentication();
+        User authUser = (User) authentication.getPrincipal();
+        // 妫�鏌ヨ繖涓敤鎴峰搴旂殑sessionId鏄惁鍜岀櫥褰曟椂鐨剆essionId鐩哥瓑
+        String loginSessionId = (String) caffeineUtil.get(CaffeineConstant.AUTH, authUser.getUsername());
+        if (! request.getSession().getId().equals(loginSessionId)) {
+            log.warn("妫�娴嬪埌鍚屼竴璐﹀彿涓や釜娴忚鍣ㄧ櫥褰�");
+            RestUtil.response(response, SystemCode.UNAUTHORIZED.getCode(), "褰撳墠鐧诲綍宸插け鏁�");
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+}
diff --git a/src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java b/src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java
index 05ba205..324a26c 100644
--- a/src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java
+++ b/src/main/java/com/ycl/jxkg/utils/CaffeineUtil.java
@@ -56,6 +56,19 @@
     }
 
     /**
+     * 鍒犻櫎
+     *
+     * @param database 鏁版嵁搴�/ caffeine map鐨刱ey
+     * @param key map鐨刱ey
+     */
+    public void remove(String database, String key) {
+        Map<String, Object> map = (Map<String, Object>) caffeineCache.getIfPresent(database);
+        if (Objects.nonNull(map)) {
+            map.remove(key);
+        }
+    }
+
+    /**
      * 鍒涘缓key
      *
      * @param database

--
Gitblit v1.8.0