feat: 实时在线用户统计

This commit is contained in:
ray
2024-08-25 08:41:08 +08:00
parent 33c8278a6a
commit b2c6babae3
5 changed files with 74 additions and 13 deletions

View File

@@ -3,9 +3,11 @@ package com.youlai.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@ConfigurationPropertiesScan
@EnableScheduling
public class SystemApplication {
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class, args);

View File

@@ -4,8 +4,12 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.service.WebsocketService;
import groovy.lang.Lazy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.Message;
@@ -32,6 +36,8 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
private WebsocketService websocketService;
/**
* 注册一个端点,客户端通过这个端点进行连接
*/
@@ -74,27 +80,24 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public Message<?> preSend(@NotNull Message<?> message, @NotNull MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
// 如果是连接请求CONNECT 命令),从请求头中取出 token 并设置到认证信息中
if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {
// 从连接头中提取授权令牌
String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
// 验证令牌格式并提取用户信息
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
try {
// 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中
if (accessor != null) {
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
bearerToken = bearerToken.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
String username = JWTUtil.parseToken(bearerToken).getPayloads().getStr(JWTPayload.SUBJECT);
if (StrUtil.isNotBlank(username)) {
accessor.setUser(() -> username);
return message;
websocketService.addUser(username);
}
} catch (Exception e) {
log.error("Failed to process authentication token.", e);
}
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
if (accessor.getUser() != null) {
String username = accessor.getUser().getName();
websocketService.removeUser(username);
}
}
}
// 不是连接请求,直接放行
return ChannelInterceptor.super.preSend(message, channel);
}
});

View File

@@ -0,0 +1,10 @@
package com.youlai.system.service;
public interface WebsocketService {
void addUser(String username);
void removeUser(String username) ;
int getOnlineUserCount() ;
}

View File

@@ -0,0 +1,44 @@
package com.youlai.system.service.impl;
import com.youlai.system.service.WebsocketService;
import groovy.lang.Lazy;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class WebsocketServiceImpl implements WebsocketService {
@Lazy
@Autowired
private SimpMessagingTemplate messagingTemplate;
private static final Set<String> onlineUsers = ConcurrentHashMap.newKeySet();
@Override
public void addUser(String username) {
onlineUsers.add(username);
}
@Override
public void removeUser(String username) {
onlineUsers.remove(username);
}
@Override
public int getOnlineUserCount() {
return onlineUsers.size();
}
@Scheduled(fixedRate = 5000)
public void sendOnlineUserCount() {
int onlineUserCount = this.getOnlineUserCount();
messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUserCount);
}
}