diff --git a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java
index 8513504d..6dfb5735 100644
--- a/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java
+++ b/virtual-patient-common/src/main/java/com/supervision/config/JwtInterceptor.java
@@ -58,27 +58,6 @@ public class JwtInterceptor implements HandlerInterceptor {
         return true;
     }
 
-    public void checkSingleLogin(String userId, JWT currentJwt) {
-
-        if (Boolean.FALSE.equals(redisTemplate.hasKey(UserTokenConstant.TOKEN_CACHE + userId))) {
-            throw new BusinessException("用户已被踢下线或超时,请重新登录", 505);
-        }
-        String value = redisTemplate.opsForValue().get(UserTokenConstant.TOKEN_CACHE + userId);
-
-        long redisCacheTime = Long.parseLong(String.valueOf(value));
-        Object currentJwtIssueTimeObject = currentJwt.getPayload("issueTime");
-        long currentJwtIssueTime = Long.parseLong(String.valueOf(currentJwtIssueTimeObject));
-        if (redisCacheTime == currentJwtIssueTime) {
-            // 如果相等,说明这个token就是最新的,直接放行
-            return;
-        } else if (currentJwtIssueTime > redisCacheTime) {
-            // 如果当前请求时间,大于Redis缓存时间,说明重新登录了,这个时候要把最新的放到缓存中
-            redisTemplate.opsForValue().set(UserTokenConstant.TOKEN_CACHE + userId, String.valueOf(System.currentTimeMillis()), 1000 * 5L, TimeUnit.MILLISECONDS);
-        } else {
-            // 走到这里,说明redisCacheTime是最新的,说明当前用户请求了一个新的token,那么原来的用户就踢掉
-            throw new BusinessException("当前用户已在其他地方登录!", 505);
-        }
-    }
 
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
diff --git a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
index 29466321..2e1d4769 100644
--- a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
+++ b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
@@ -1,9 +1,6 @@
 package com.supervision.constant;
 
 public interface UserTokenConstant {
-
-    String TOKEN_CACHE = "USER:LOGIN:TOKEN:";
-
     String USER_ID_CACHE = "USER:ID:CACHE";
 
     String KICK_CHANNEL = "USER:KICK:CHANNEL";
diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java
index 0af12668..c692e824 100644
--- a/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java
+++ b/virtual-patient-web/src/main/java/com/supervision/controller/UserController.java
@@ -43,19 +43,9 @@ public class UserController {
             throw new BusinessException("密码错误");
         }
         String token = TokenUtil.creatToken(JSONUtil.toJsonStr(user.get()));
-        // 将用户的token保存起来,超时时间为5分钟
-        redisTemplate.opsForValue().set(UserTokenConstant.TOKEN_CACHE + user.get().getId(), String.valueOf(System.currentTimeMillis()), 1000 * 5L, TimeUnit.MILLISECONDS);
         return token;
     }
 
-    @ApiOperation("token心跳")
-    @PostMapping("keepaliveToken")
-    public void keepaliveToken() {
-        User user = UserUtil.getUser();
-        // 每次心跳都设置为5分钟之后
-        redisTemplate.expire(UserTokenConstant.TOKEN_CACHE + user.getId(), 1000 * 5L, TimeUnit.MILLISECONDS);
-    }
-
     @ApiOperation("踢用户下线")
     @GetMapping("kickUser")
     public void kickUser(String userId) {
diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
index 1a122a26..05b1b078 100644
--- a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
+++ b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
@@ -1,7 +1,10 @@
 package com.supervision.controller;
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
 import com.supervision.constant.UserTokenConstant;
+import com.supervision.usermanage.UserWebSocketDTO;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -29,10 +32,12 @@ public class WebSocketServer {
      * 有客户端连接成功
      */
     @OnOpen
-    public void onOpen(Session session, @PathParam(value = "uid") String uid) {
+    public void onOpen(Session session, @PathParam(value = "uid") String uid) throws IOException {
         log.info("用户:{}登录,缓存到Redis", uid);
+        // 链接之前先把之前的用户踢下线(ignoreSessionId防止把当前用户踢下线)
+        redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(uid, session.getId())));
         SESSION_POOL.put(uid, session);
-        redisTemplate.opsForSet().add(UserTokenConstant.USER_ID_CACHE, uid);
+        redisTemplate.opsForHash().put(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
     }
 
     /**
@@ -40,9 +45,9 @@ public class WebSocketServer {
      */
     @OnClose
     public void onClose(Session session, @PathParam(value = "uid") String uid) {
-        redisTemplate.opsForSet().remove(UserTokenConstant.USER_ID_CACHE, uid);
+        redisTemplate.opsForHash().delete(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
         SESSION_POOL.remove(uid);
-        log.info("用户:{}关闭,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForSet().size(UserTokenConstant.USER_ID_CACHE));
+        log.info("用户:{}关闭,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE));
     }
 
     /**
@@ -50,18 +55,20 @@ public class WebSocketServer {
      */
     @OnError
     public void onError(Session session, @PathParam(value = "uid") String uid, Throwable throwable) {
-        redisTemplate.opsForSet().remove(UserTokenConstant.USER_ID_CACHE, uid);
+        redisTemplate.opsForHash().delete(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
         SESSION_POOL.remove(uid);
-        log.error("用户:{}发生错误,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForSet().size(UserTokenConstant.USER_ID_CACHE), throwable);
+        log.error("用户:{}发生错误,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE), throwable);
     }
 
     // 实现一个方法用于踢下线用户,走的是Redis的消息队列
-    public void kickUser(String userId) throws IOException {
+    public void kickUser(String userId, String ignoreSessionId) throws IOException {
         log.info("尝试主动踢用户:{}下线", userId);
         Session session = SESSION_POOL.get(userId);
-        if (ObjectUtil.isNotEmpty(session)) {
+        // 只有不是忽略剔除的sessionId才可以踢下线
+        if (ObjectUtil.isNotEmpty(session) && !StrUtil.equals(ignoreSessionId, session.getId())) {
             session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "用户被踢下线"));
-            log.info("主动踢用户:{}下线成功", userId);
+            SESSION_POOL.remove(userId);
+            log.info("主动踢用户:{},sessionId:{} 下线成功", userId, session.getId());
             return;
         }
         log.info("主动踢用户:{}下线,未找到用户,踢下线失败", userId);
diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
index 0996558b..586c1eda 100644
--- a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
+++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
@@ -23,11 +23,8 @@ public class DiagnoseHallServiceImpl implements DiagnoseHallService {
     @Override
     public boolean achieveDiagnoseResource() {
         long humanMaxNumber = Long.parseLong(resourceNumber);
-        Long currentUserNum = redisTemplate.opsForSet().size(UserTokenConstant.USER_ID_CACHE);
+        long currentUserNum = redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE);
         // 如果小于数字人最大连接数,则可以连接
-        if (null == currentUserNum){
-            return true;
-        }
         return currentUserNum <= humanMaxNumber;
     }
 }
diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java b/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java
index b2fc923e..c1311808 100644
--- a/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java
+++ b/virtual-patient-web/src/main/java/com/supervision/usermanage/KickUserListener.java
@@ -1,5 +1,6 @@
 package com.supervision.usermanage;
 
+import cn.hutool.json.JSONUtil;
 import com.supervision.constant.UserTokenConstant;
 import com.supervision.controller.WebSocketServer;
 import lombok.extern.slf4j.Slf4j;
@@ -20,10 +21,11 @@ public class KickUserListener implements MessageListener {
 
     @Override
     public void onMessage(Message message, byte[] pattern) {
-        String userId = message.toString();
-        log.info("Redis的Channel:{}收到踢用户下线消息:{}", UserTokenConstant.KICK_CHANNEL, userId);
+        String messageString = message.toString();
+        UserWebSocketDTO user = JSONUtil.toBean(messageString, UserWebSocketDTO.class);
+        log.info("Redis的Channel:{}收到踢用户{}下线消息", UserTokenConstant.KICK_CHANNEL, user.getUserId());
         try {
-            webSocketServer.kickUser(userId);
+            webSocketServer.kickUser(user.getUserId(), user.getIgnoreSessionId());
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java
new file mode 100644
index 00000000..42a50b7a
--- /dev/null
+++ b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java
@@ -0,0 +1,16 @@
+package com.supervision.usermanage;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class UserWebSocketDTO {
+
+    private String userId;
+
+    /**
+     * 忽略提除的ID
+     */
+    private String ignoreSessionId;
+}