单设备登录
SDL(Single Device Login)是一种单设备登录的机制,它允许用户在同一时间只能在一个设备上登录,当用户在其他设备上登录时,之前登录的设备会被挤下线。
应用场景
- 视频影音,防止一个账号共享,防止一些账号贩子
- 社交媒体平台:社交媒体平台通常有多种安全措施来保护用户账户,其中之一就是单设备登录。这样可以防止他人在未经授权的情况下访问用户的账户,并保护用户的个人信息和隐私
- 对于在线购物和电子支付平台,用户的支付信息和订单详情是敏感的。通过单设备登录,可以在用户进行支付操作时增加额外的安全层级,确保只有授权设备可以进行支付操作
- 对于电子邮箱和通讯应用,用户的个人和机密信息都存储在其中。通过单设备登录机制,可以确保用户的电子邮箱或通讯应用只能在一个设备上登录,避免账户被他人恶意使用
实现思路
设计数据结构
{
id:{
socket:ws实例
fingerprint:浏览器指纹
}
}
- 第一次登录的时候记录用户id,并且记录socket信息,和浏览器指纹
- 当有别的设备登录的时候发现之前已经连接过了,便使用旧的socket发送下线通知,并且关闭旧的socket,更新socket替换成当前新设备的ws连接
浏览器指纹
指纹技术有很多种,这里采用canvas指纹技术
实现代码
nodejs端
import express from 'express'
import { WebSocketServer } from 'ws'
import cors from 'cors'
const app = express()
app.use(cors())
app.use(express.json())
//存放数据结构
const connection = {}
const server = app.listen(3000)
const wss = new WebSocketServer({ server })
wss.on('connection', (ws) => {
ws.on('message', (message) => {
const data = JSON.parse(message)
if (data.action === 'login') {
if (connection[data.id] && connection[data.id].fingerprint) {
console.log('账号在别处登录')
//提示旧设备
connection[data.id].socket.send(JSON.stringify({
action:'logout',
message:`你于${new Date().toLocaleString()}账号在别处登录`
}))
connection[data.id].socket.close() //断开旧设备连接
connection[data.id].socket = ws //更新ws
} else {
console.log('首次登录')
connection[data.id] = {
socket: ws, //记录ws
fingerprint: data.fingerprint //记录指纹
}
}
}
})
})
浏览器端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>SDL</h1>
<script src="./md5.js"></script>
<script>
//浏览器指纹
const createBrowserFingerprint = () => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 1, 1)
return md5(canvas.toDataURL())
}
//谷歌abf12f62e03d160f7f24144ef1778396
//火狐80bea69bfc7cad5832d12e41714cf677
//Edge abf12f62e03d160f7f24144ef1778396
const ws = new WebSocket('ws://192.168.120.145:3000') //socket本地IP+端口
ws.addEventListener('open', () => {
ws.send(JSON.stringify({
action: 'login', //动作登录
id: 1, //用户ID
fingerprint: createBrowserFingerprint() //浏览器指纹
}))
})
ws.addEventListener('message', (message) => {
const data = JSON.parse(message.data)
if (data.action === 'logout') {
alert(data.message) //监听到挤下线操作提示弹框
}
})
</script>
</body>
</html>