在重新连接之前刷新令牌



我正在用 Socket.io 创建一个Electron应用程序。当用户的计算机进入睡眠模式时,服务器会与客户端断开连接,引发错误"传输关闭"。当用户尝试重新连接时,我会检查令牌是否仍然有效,如果无效,我会刷新它们,并尝试将它们发送到socketIo服务器。

我遇到的问题是,在"reconnect_attempt"socket.io 不会等到我刷新令牌才尝试重新连接,它会立即尝试使用旧令牌重新连接,这些令牌被服务器拒绝,这似乎也会终止与用户的连接,阻碍未来的重新连接尝试。

这是我连接到服务器的代码的一部分

module.exports.connect = async (JWT) => {
return new Promise( async resolve => {
console.log("connecting to the server")
const connectionOptions = {
secure: true,
query: {token: JWT},
reconnectionDelay: 4000
}
let socket = await socketIo.connect(`${process.env.SERVER_URL}:${process.env.SERVER_PORT}`, connectionOptions);
resolve(socket)
})
}

这是我的reconnect_attempt代码

socket.on('reconnect_attempt', async () => {
const getCurrentJWT = require("../../main").getCurrentJWT;
let JWT = await getCurrentJWT(); //By the time this line returns, socket.io has already tried to reconnect

if(JWT.success) { //if refreshed successfully
console.log("Trying to submit new token......", JWT);
socket.query.token = JWT.JWT;
} else {
console.log("Token not refreshed.")
}
});

这是我在服务器上拥有的一部分

io.use(async (socket, next) => {
let token = socket.handshake.query.token;
//and the instruction from here https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
let tokenIsValid = await checkTokenValidity(token);
if( tokenIsValid ) {
next();
} else {
next(new Error('invalidToken'));
console.log("Not valid token")
}
})

在您的服务器中具有以下内容。

io.use( async function(socket, next) {
let address = socket.handshake.address;
run++; // 0 -> 1
// Validate Token
const token = socket.handshake.auth.token;
if(token !== undefined){
try{
await tokenVerify(token).then((payload) => {
const serverTimestamp = Math.floor(Date.now() / 1000);
const clientTimestamp = payload.exp;
if(clientTimestamp > serverTimestamp){
console.log("Connection from: " + address + " was accepted");
console.log("Token [" + token + "] from: " + address + " was accepted");
next();
}else{
console.log("Connection from: " + address + " was rejected");
console.log("Token [" + token + "] from: " + address + " was rejected");
next(new Error("unauthorized"));
}
});
}catch (e) {
console.log(e);
}
}
})

使用上面的代码,如果令牌无效,服务器将响应">未经授权"。

因此,在客户端,我们可以捕获该消息,如下所示。

socket_io.on("connect_error", (err) => {
if(err?.message === 'unauthorized'){
var timeout = (socket_reconnection_attempts === 0 ? 5000 : 60000)
console.log("Trying to reconnect in the next " + (timeout / 1000) + ' seconds')
setTimeout(function (){
console.log('Trying to reconnect manually')
socket_reconnection_attempts++;
loadAuthToken().then(function (token) {
socket_io.auth.token = token;
socket_io.connect();
})
}, timeout)
}
});

使用上面的代码,仅当错误消息为">未经授权"时,客户端才会尝试重新连接并刷新令牌。

变量">socket_reconnection_attempts"是为了避免在短时间内发送大量重新连接尝试。

简而言之,您可以使用身份验证。

连接时:

auth: {
token: token 
} 

在重新连接时

socket.auth.token = "NEW_TOKEN";
socket.connect();

我可以为此共享套接字io实现,您可以根据需要对其进行修改。

对于客户端,

let unauthorized = false;
let socket = io.connect('ws://localhost:8080', {
transports: ["websocket"],
auth: {
token: GET_YOUR_TOKEN()
}
});
socket.on("connect", () => {
unauthorized = false;
});
socket.on('UNAUTHORIZED', () => {
unauthorized = true;
});
socket.on("disconnect", (reason) => {
if (reason === "io server disconnect") {
if(unauthorized) {
socket.auth.token = token;
} 
socket.connect();
}
});
socket.on('PING', ()=>{
socket.emit('PONG', token);
});

对于服务器端

io.on("connection", (socket) => {
socket.on('PONG', function (token) {
if (isValidToken(token) == false) {
socket.emit("UNAUTHORIZED");
socket.disconnect();
}
});
setInterval(() => {
socket.emit('PING');
}, <YOUR-TIME>);
});

最新更新