|
|
@@ -39,7 +39,10 @@ public class VehicleControlEventHandler {
|
|
|
@OnConnect
|
|
|
public void onConnect(SocketIOClient client) {
|
|
|
String sessionId = client.getSessionId().toString();
|
|
|
- log.info("客户端连接: sessionId={}, remoteAddress={}", sessionId, client.getRemoteAddress());
|
|
|
+ log.info("客户端连接: sessionId={}, remoteAddress={}, engineIOVersion={}, handshakeData={}",
|
|
|
+ sessionId, client.getRemoteAddress(),
|
|
|
+ client.getEngineIOVersion() != null ? client.getEngineIOVersion().toString() : "null",
|
|
|
+ client.getHandshakeData() != null ? client.getHandshakeData().getUrlParams().toString() : "null");
|
|
|
|
|
|
// 1. OAuth2 JWT Token 校验
|
|
|
String token = getTokenFromClient(client);
|
|
|
@@ -48,27 +51,63 @@ public class VehicleControlEventHandler {
|
|
|
token != null && token.length() > 20 ? token.substring(0, 20) + "..." : token);
|
|
|
|
|
|
if (StrUtil.isEmpty(token)) {
|
|
|
- log.warn("客户端连接失败: 未提供 Token, sessionId={}", sessionId);
|
|
|
- log.debug("Handshake Headers: {}", client.getHandshakeData().getHttpHeaders());
|
|
|
- log.debug("Handshake URL Params: {}", client.getHandshakeData().getUrlParams());
|
|
|
+ log.error("客户端连接失败: 未提供 Token, sessionId={}", sessionId);
|
|
|
+ log.error("Handshake Headers: {}", client.getHandshakeData().getHttpHeaders());
|
|
|
+ log.error("Handshake URL Params: {}", client.getHandshakeData().getUrlParams());
|
|
|
+ log.error("Handshake URL: {}", client.getHandshakeData().getUrl());
|
|
|
+ // 尝试发送错误事件(如果连接已建立)
|
|
|
+ try {
|
|
|
+ client.sendEvent("connect_error", "Token is required");
|
|
|
+ Thread.sleep(100); // 确保事件发送完成
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("发送错误事件失败(连接可能未完全建立): {}", e.getMessage());
|
|
|
+ }
|
|
|
client.disconnect();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 2. 本地验证 JWT(验证签名 + 过期时间)
|
|
|
DecodedJWT jwt = null;
|
|
|
+ String errorMessage = null;
|
|
|
try {
|
|
|
jwt = OAuth2JwtUtil.verifyToken(token);
|
|
|
if (jwt == null) {
|
|
|
log.warn("客户端连接失败: OAuth2 JWT Token 验证失败(返回 null), sessionId={}, tokenLength={}",
|
|
|
sessionId, token.length());
|
|
|
// 注意:OAuth2JwtUtil.verifyToken 内部已经记录了详细的验证失败原因
|
|
|
+ // 尝试判断是否是过期(通过解析 Token 的 exp 字段,但注意不能完全验证签名)
|
|
|
+ errorMessage = "Token expired or invalid";
|
|
|
+ try {
|
|
|
+ // 尝试解析 Token 判断是否过期(不验证签名)
|
|
|
+ com.auth0.jwt.interfaces.DecodedJWT decodedWithoutVerify = com.auth0.jwt.JWT.decode(token);
|
|
|
+ if (decodedWithoutVerify.getExpiresAt() != null && decodedWithoutVerify.getExpiresAt().before(new java.util.Date())) {
|
|
|
+ errorMessage = "Token expired";
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 解析失败,使用默认错误信息
|
|
|
+ log.debug("无法解析 Token 判断过期时间: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ // 尝试发送错误事件(如果连接已建立)
|
|
|
+ try {
|
|
|
+ client.sendEvent("connect_error", errorMessage);
|
|
|
+ Thread.sleep(100); // 确保事件发送完成
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("发送错误事件失败(连接可能未完全建立): {}", e.getMessage());
|
|
|
+ }
|
|
|
client.disconnect();
|
|
|
return;
|
|
|
}
|
|
|
log.info("Token 验证成功: sessionId={}, userId={}", sessionId, OAuth2JwtUtil.getUserId(jwt));
|
|
|
} catch (Exception e) {
|
|
|
log.error("客户端连接失败: OAuth2 JWT Token 验证异常, sessionId={}, error={}", sessionId, e.getMessage(), e);
|
|
|
+ errorMessage = "Token verification failed: " + e.getMessage();
|
|
|
+ // 尝试发送错误事件(如果连接已建立)
|
|
|
+ try {
|
|
|
+ client.sendEvent("connect_error", errorMessage);
|
|
|
+ Thread.sleep(100); // 确保事件发送完成
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.debug("发送错误事件失败(连接可能未完全建立): {}", ex.getMessage());
|
|
|
+ }
|
|
|
client.disconnect();
|
|
|
return;
|
|
|
}
|
|
|
@@ -79,6 +118,14 @@ public class VehicleControlEventHandler {
|
|
|
|
|
|
if (userId == null) {
|
|
|
log.warn("客户端连接失败: 无法从 Token 中获取用户ID, sessionId={}", sessionId);
|
|
|
+ errorMessage = "Invalid token: user ID not found";
|
|
|
+ // 尝试发送错误事件(如果连接已建立)
|
|
|
+ try {
|
|
|
+ client.sendEvent("connect_error", errorMessage);
|
|
|
+ Thread.sleep(100); // 确保事件发送完成
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("发送错误事件失败(连接可能未完全建立): {}", e.getMessage());
|
|
|
+ }
|
|
|
client.disconnect();
|
|
|
return;
|
|
|
}
|
|
|
@@ -99,29 +146,70 @@ public class VehicleControlEventHandler {
|
|
|
/**
|
|
|
* 从客户端获取 Token
|
|
|
* SocketIO 连接时,Token 可以通过以下方式传递:
|
|
|
- * 1. URL 参数:?token=xxx
|
|
|
- * 2. Handshake 数据
|
|
|
+ * 1. URL 参数:?token=xxx 或 Authorization=Bearer xxx
|
|
|
+ * 2. HTTP Headers:Authorization: Bearer xxx
|
|
|
+ *
|
|
|
+ * 注意:Engine.IO v1 客户端通常通过 URL 参数传递 token
|
|
|
*/
|
|
|
private String getTokenFromClient(SocketIOClient client) {
|
|
|
- // 方式1:从 Handshake 数据中获取(HTTP Headers)
|
|
|
+ String sessionId = client.getSessionId().toString();
|
|
|
+
|
|
|
+ // 方式1:从 HTTP Headers 中获取(Authorization header)
|
|
|
String authHeader = client.getHandshakeData().getHttpHeaders().get("Authorization");
|
|
|
- if (StrUtil.isNotEmpty(authHeader) && authHeader.startsWith("Bearer ")) {
|
|
|
- return authHeader.substring(7);
|
|
|
+ if (StrUtil.isNotEmpty(authHeader)) {
|
|
|
+ log.debug("从 HTTP Headers 获取 Authorization: sessionId={}, header={}", sessionId, authHeader.substring(0, Math.min(50, authHeader.length())));
|
|
|
+ if (authHeader.startsWith("Bearer ")) {
|
|
|
+ String token = authHeader.substring(7);
|
|
|
+ if (StrUtil.isNotEmpty(token)) {
|
|
|
+ log.debug("从 Authorization Header 获取到 Token: sessionId={}, tokenLength={}", sessionId, token.length());
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 方式2:从 URL 参数中获取(URL Params 返回的是 List<String>)
|
|
|
Map<String, java.util.List<String>> urlParams = client.getHandshakeData().getUrlParams();
|
|
|
+ if (urlParams != null && !urlParams.isEmpty()) {
|
|
|
+ log.debug("URL Params: sessionId={}, params={}", sessionId, urlParams.keySet());
|
|
|
+
|
|
|
+ // 尝试获取 token 参数
|
|
|
java.util.List<String> tokenList = urlParams.get("token");
|
|
|
if (tokenList != null && !tokenList.isEmpty()) {
|
|
|
String token = tokenList.get(0);
|
|
|
+ if (StrUtil.isNotEmpty(token)) {
|
|
|
+ log.debug("从 URL Params[token] 获取到 Token: sessionId={}, tokenLength={}", sessionId, token.length());
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试获取 Authorization 参数(客户端可能通过 URL 参数传递 Authorization)
|
|
|
+ java.util.List<String> authList = urlParams.get("Authorization");
|
|
|
+ if (authList != null && !authList.isEmpty()) {
|
|
|
+ String auth = authList.get(0);
|
|
|
+ if (StrUtil.isNotEmpty(auth) && auth.startsWith("Bearer ")) {
|
|
|
+ String token = auth.substring(7);
|
|
|
+ if (StrUtil.isNotEmpty(token)) {
|
|
|
+ log.debug("从 URL Params[Authorization] 获取到 Token: sessionId={}, tokenLength={}", sessionId, token.length());
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方式3:从 Single URL 参数中获取(直接调用 API)
|
|
|
+ try {
|
|
|
+ String token = client.getHandshakeData().getSingleUrlParam("token");
|
|
|
if (StrUtil.isNotEmpty(token)) {
|
|
|
+ log.debug("从 getSingleUrlParam(token) 获取到 Token: sessionId={}, tokenLength={}", sessionId, token.length());
|
|
|
return token;
|
|
|
}
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.debug("getSingleUrlParam 异常: sessionId={}, error={}", sessionId, e.getMessage());
|
|
|
}
|
|
|
|
|
|
- // 方式3:从 Single 参数中获取
|
|
|
- String token = client.getHandshakeData().getSingleUrlParam("token");
|
|
|
- return token;
|
|
|
+ // 方式4:尝试从所有 URL 参数中查找(可能参数名不同)
|
|
|
+ log.warn("未找到 Token: sessionId={}, 请检查客户端是否正确传递 token", sessionId);
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/**
|