扫码登录
SCL (Scan Code Login) 是一种扫码登录的技术,它允许用户通过扫描二维码来进行登录操作。这种登录方式在许多应用和网站中得到广泛应用,因为它简单、方便且安全。
SCL 扫码登录的优点包括:
- 方便快捷:用户只需打开扫码应用程序并扫描二维码即可完成登录,无需手动输入用户名和密码。
- 安全性高:扫码登录采用了加密技术,用户的登录信息在传输过程中得到保护,降低了密码被盗取或泄露的风险。
- 避免键盘记录:由于用户无需在登录过程中输入敏感信息,如用户名和密码,因此不会受到键盘记录软件的威胁。
- 适用性广泛:SCL 扫码登录可以与不同的应用和网站集成,提供统一的登录方式,使用户无需记住多个账户的用户名和密码。
实现流程
安装的依赖
- express 提供接口服务
- jsonwebtoken 生成token
- qrcode 生成二维码
流程图
大体逻辑
js
const status = {
0: '未授权',
1: '已授权',
2: '超时'
}
- 需要一个页面调用接口获取qrcode也就是二维码去展示,然后顺便展示一下状态,默认0 未授权
- 在这个页面轮询接口检查状态是否是已授权,如果是已授权或者超时就停止轮询。
- 扫码之后会打开授权页面,在授权页面点击确认按钮进行授权分配token
代码实现
目录结构
public
- mandate.html 授权页面
- qrcode.html 二维码页面
index.js nodejs逻辑代码
index.js
js
import express from 'express'
import qrcode from 'qrcode'
import jwt from 'jsonwebtoken'
let user = {
}
let userId = 1 //模拟一个用户
const app = express()
app.use(express.json())
app.use('/static', express.static('public')) //初始化静态目录
//初始化数据结构 记录用户和创建二维码的时间
//并且生成二维码的时候使用的是授权的那个页面并且把用户id带过去
app.get('/qrcode', async (req, res) => {
user[userId] = {
token: null,
time: Date.now()
}
const code = await qrcode.toDataURL(`http://192.168.120.145:3000/static/mandate.html?userId=${userId}`)
res.json({
code,
userId
})
})
//授权确认接口 陈功授权之后生成token
app.post('/login/:userId', (req, res) => {
const token = jwt.sign(req.params.userId, 'secret')
user[req.params.userId].token = token
user[req.params.userId].time = Date.now()
res.json({
token
})
})
//检查接口 这个接口要被轮询调用检查状态,0未授权 1已授权 2超时
app.get('/check/:userId', (req, res) => {
//判断超时时间
if (Date.now() - user[userId].time > 1000 * 60 * 1) {
return res.json({
status: 2
})
}
//如果有token那就是验证成功
else if (user[1].token) {
return res.json({
status: 1
})
} else {
return res.json({
status: 0
})
}
})
app.listen(3000, () => {
console.log('http://localhost:3000')
})
qrcode.html
html
<!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>
<img id="qrcode" src="" alt="">
<div id="status-div"></div>
<script>
const status = {
0: '未授权',
1: '已授权',
2: '超时'
}
const qrcode = document.getElementById('qrcode')
const statusDiv = document.getElementById('status-div')
let userId = null
statusDiv.innerText = status[0]
fetch('/qrcode').then(res => res.json()).then(res => {
qrcode.src = res.code //获取二维码
userId = res.userId //获取用户id
let timer = setInterval(() => {
//轮询调用检查接口
fetch(`/check/${userId}`).then(res => res.json()).then(res => {
statusDiv.innerText = status[res.status]
//如果返回的状态是 超时 或者是已授权 就停止轮训
if (res.status != 0) {
clearInterval(timer)
}
})
}, 1000)
})
</script>
</body>
</html>
mandate.html
html
<!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>
<div> <button id="btn" style="width: 100%;height: 50px;">同意授权</button></div>
<div> <button style="width: 100%;height: 50px;margin-top: 20px;">拒绝授权</button></div>
<script>
const btn = document.getElementById('btn')
let userId = location.search.slice(1).split('=')[1]
btn.onclick = () => {
//点击授权按钮
fetch(`/login/${userId}`, {
method: 'POST',
}).then(res => res.json()).then(res => {
alert(`授权成功`)
}).catch(err => {
alert(err)
})
}
</script>
</body>
</html>