使用WebSocket实现扫码登录

98 阅读3分钟

1、开启一个服务

创建一个文件夹(dd-login),npm init -y初始化,安装ws依赖(npm i ws),启动服务(node app.js)

app.js

// 安装依赖:npm install ws
const WebSocket = require('ws')

const wss = new WebSocket.Server({ port: 5000 }) // 启动 WebSocket 服务,监听 5000 端口

console.log('WebSocket 服务已启动,端口 5000')

wss.on('connection', ws => {
  console.log('前端已连接')

  // 监听前端发送的消息
  ws.on('message', message => {
    console.log(`收到前端消息: ${message}`)
    const data = JSON.parse(message)

    if (data.sessionId) {
      console.log(`收到 sessionId:${data.sessionId}`)

      // 模拟扫码结果推送,稍后一段时间发送登录结果
      simulateLoginProcess(ws)
    }
  })

  // 当前端断开连接
  ws.on('close', () => {
    console.log('前端断开连接')
  })

  ws.on('error', error => {
    console.error('WebSocket 错误:', error)
  })
})

// 模拟后台的登录流程
function simulateLoginProcess(ws) {
  setTimeout(() => {
    // 发送“pending”状态
    ws.send(JSON.stringify({ status: 'pending', message: '等待用户扫码' }))
  }, 1000)

  // 模拟一个扫码完成后的状态变更
  setTimeout(() => {
    ws.send(JSON.stringify({ status: 'success', message: '扫码登录成功' }))
  }, 5000) // 延迟 5 秒发送“成功”状态
}

2、模拟扫码登录

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>钉钉扫码登录 Demo</title>
    <style>
      #status {
        margin: 20px 0;
        font-size: 16px;
        color: blue;
      }
      #qr-code {
        width: 200px;
        height: 200px;
        margin: 20px auto;
        border: 1px solid #000;
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <h1>钉钉扫码登录 Demo</h1>

    <!-- 显示状态 -->
    <div id="status">点击下方按钮以生成二维码进行登录</div>

    <!-- 模拟二维码显示 -->
    <div id="qr-code">二维码</div>

    <!-- 按钮:触发扫码登录 -->
    <button id="connect">生成登录二维码</button>

    <script>
      let socket

      document.getElementById('connect').addEventListener('click', () => {
        // 打开 WebSocket 连接
        if (!socket || socket.readyState !== WebSocket.OPEN) {
          connectWebSocket()
        }
      })

      // 连接 WebSocket 的逻辑
      function connectWebSocket() {
        const sessionId = generateSessionId() // 模拟生成唯一的 sessionId

        // 创建 WebSocket 连接
        socket = new WebSocket('ws://192.168.1.37:5000')

        // 当连接打开时发送数据
        socket.onopen = () => {
          console.log('WebSocket 连接已建立')
          document.getElementById('status').innerText = '生成二维码中...'

          // 向后端发送 sessionId
          socket.send(JSON.stringify({ sessionId }))
        }

        // 接收后端推送的消息
        socket.onmessage = event => {
          const data = JSON.parse(event.data)
          console.log('接收到消息:', data)

          // 根据状态展示不同的信息
          if (data.status === 'pending') {
            document.getElementById('status').innerText = data.message
            document.getElementById('qr-code').innerText = '等待扫码...'
          } else if (data.status === 'success') {
            document.getElementById('status').innerText = '登录成功!'
            document.getElementById('qr-code').innerText = '已登录'
            socket.close() // 登录成功后关闭连接
          }
        }

        // 监听 WebSocket 错误
        socket.onerror = error => {
          console.error('WebSocket 错误:', error)
          document.getElementById('status').innerText = '连接失败,请稍后重试!'
        }

        // 监听连接关闭
        socket.onclose = () => {
          console.log('WebSocket 连接已关闭')
        }
      }

      // 模拟生成一个唯一的 sessionId
      function generateSessionId() {
        return 'session_' + Math.random().toString(36).substring(2, 15)
      }
    </script>
  </body>
</html>

效果:

动画.gif

流程:

  1. 点击按钮打开弹框,此时调接口通过后端获取二维码图片,同时后端启动WebSocket服务
  2. 前端在拿到二维码图片后,一方面显示二维码在页面上,一方面初始化WebSocket实例,设置message事件,同时接口应该返回一个id,每次向后端发送消息时需要携带这个id
  3. 用户进行扫码时,后端识别到用户扫码操作,发送一个消息告诉前端,用户开始扫码了,前端可以显示用户扫码成功,请在手机上确认登录
  4. 用户在手机上点击确认登录后,后端发送一个消息给前端,一般通过一个状态码表示用户点击了确认登录了,前端在监听到这个状态码,就可以将页面显示为已登录状态,跳转到首页
  5. 在通信开始时实例化WebSocket,在通信结束后需要关闭WebSocket