SDL单设备登录
SDL单设备登录
需求:当账户在其他地方登录时,当前已登录账户会被顶下线
核心思路:
前后端建立WebSocket连接,当客户端登录账户,根据用户id保存Socket连接,如果该用户已有保存的连接,则通知下线并断开,然后更新连接
实现
Node 服务端
创建sdl.js实现核心逻辑
const { WebSocketServer } = require('ws')
const jwt = require('jsonwebtoken');
// 当前连接设备
let connection = {}
let wss = null
function setupSDL(server) {
try {
wss = new WebSocketServer({ server })
wss.on('connection', (ws) => {
ws.on('message', (message) => {
const data = JSON.parse(message)
if (data.action === 'login' && data.token) {
let verifyResult = null
jwt.verify(data.token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
console.log(err)
return
}
verifyResult = decoded
});
if (!verifyResult) {
return
}
if (connection[data.id] && connection[data.id].finggerprint) {
console.log(`设备${data.id}已经登录`)
// 设备已经登录
connection[data.id].socket.send(JSON.stringify({
action: 'logout',
message: '您的账户已在其他设备登录,请重新登录'
}))
connection[data.id].socket.close()
connection[data.id].socket = ws
connection[data.id].token = data.token
connection[data.id].finggerprint = data.finggerprint
} else {
connection[data.id] = {
// 设备连接socket
socket: ws,
// token
token: data.token,
// 设备唯一标识
finggerprint: data.finggerprint
}
console.log(`设备${data.id}登录成功`)
}
}
})
})
} catch (error) {
console.log(error)
}
}
module.exports = {
setupSDL,
connection,
wss
}
// 启动服务器
let server = app.listen(PORT, () => {
console.log(`服务器正在运行在:http://localhost:${PORT}`);
});
setupSDL(server); // 初始化sdl
客户端
项目初始化后new WebSocket连接,并监听登出的消息。每次用户登录都发送登录消息,如果当前已登录了发送登录消息(防止刷新丢失状态)
//浏览器指纹
const createBrowserFingerprint = () => {
const canvas = document.createElement('canvas')
const ctx: CanvasRenderingContext2D|null = canvas.getContext('2d')
if (!ctx) {
return
}
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 1, 1)
// return md5(canvas.toDataURL())
console.log(canvas.toDataURL())
return canvas.toDataURL()
}
var ws : WebSocket|null = null;
const connectionSdl = () => {
ws = new WebSocket('ws://localhost:3200') //socket本地IP+端口
ws.addEventListener('open', () => {
// 已登录
if(userStore.userInfo?.id && getToken()){
ws!.send(JSON.stringify({
action: 'login',
id: userStore.userInfo.id,
token: getToken(),
finggerprint: createBrowserFingerprint()
}))
}
})
ws.addEventListener('message', (event) => {
console.log(event.data)
const data = JSON.parse(event.data)
if (data.action === 'logout') {
// 退出登录
userStore.userLogout()
ElMessage.error('您的账号在其他地方登录,请重新登录')
// closeDialog()
}
})
}
connectionSdl()
每次用户登录都发送登录消息
if(ws && ws.readyState === 1){
ws.send(JSON.stringify({
action: 'login',
id: userStore.userInfo.id,
token: getToken(),
finggerprint: createBrowserFingerprint()
}))
}
实现效果
用我自己写的一套项目的效果进行展示