转载自: https://juejin.cn/post/7389952503041884170
前言
二维码其实是一个包含参数的链接,二维码登录就是通过用包含登录态的手机扫描二维码,将参数和token传递给后端,请求授权登录到展示二维码系统,从而实现登录。
二维码登录让用户省去了输入账号密码的麻烦。
登录简单点来说可以概括为俩点
下面我们就会围绕着这俩点来展开详细说明
原理解析
其实大部分的二维码就是一个url地址
我们以掘金扫码登录为例来进行剖析

我们进行一个解析

juejin.cn/app?next_ur…
我们可以发现它实际就是这样的一个url
整个三端交互的过程大概是这样子的:

流程概述
简单来说氛围下面的步骤:
- PC端:进入二维码登录页面,请求服务端获取二维码的ID。
- 服务端:生成二维码ID,并将其与请求的设备绑定后,返回有效的二维码ID。
- PC端:根据二维码ID生成二维码图片,并展示出来。
- 移动端:扫描二维码,解析出二维码ID。
- 移动端:使用移动端的token和二维码ID请求服务端进行登录。
- 服务端:解析验证请求,绑定用户信息,并返回给移动端一个用于二次确认的临时token。
- PC端:展示二维码为“待确认”状态。
- 移动端:使用二维码ID、临时token和移动端的token进行确认登录。
- 服务端:验证通过后,修改二维码状态,并返回给PC端一个登录的token。
下面我们来用一个python的代码来描述一下这个过程。
首先是服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| from flask import Flask, request, jsonify import uuid import time app = Flask(__name__)
qr_code_store = {} temporary_tokens = {} @app.route('/generate_qr', methods=['POST']) def generate_qr(): device_id = request.json['device_id'] qr_id = str(uuid.uuid4()) qr_code_store[qr_id] = {'device_id': device_id, 'timestamp': time.time(), 'status': 'waiting'} return jsonify({'qr_id': qr_id}) @app.route('/scan_qr', methods=['POST']) def scan_qr(): qr_id = request.json['qr_id'] token = request.json['token'] if qr_id in qr_code_store: qr_code_store[qr_id]['status'] = 'scanned' temp_token = str(uuid.uuid4()) temporary_tokens[temp_token] = {'qr_id': qr_id, 'timestamp': time.time()} return jsonify({'temp_token': temp_token}) return jsonify({'error': 'Invalid QR code'}), 400 @app.route('/confirm_login', methods=['POST']) def confirm_login(): qr_id = request.json['qr_id'] temp_token = request.json['temp_token'] mobile_token = request.json['mobile_token'] if temp_token in temporary_tokens and temporary_tokens[temp_token]['qr_id'] == qr_id: login_token = str(uuid.uuid4()) qr_code_store[qr_id]['status'] = 'confirmed' return jsonify({'login_token': login_token}) return jsonify({'error': 'Invalid confirmation'}), 400 if __name__ == '__main__': app.run(debug=True)
|
之后来看PC端(这里应该用H5页面比较合适):
1 2 3 4 5 6 7 8 9 10 11 12 13
| import requests import json
response = requests.post('http://localhost:5000/generate_qr', json={'device_id': 'PC_device'}) qr_id = response.json()['qr_id']
print(f"QR Code ID: {qr_id}")
print("QR Code Status: Waiting for confirmation")
|
之后再来看移动端的代码 (这里也用python模拟发起请求):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import requests
qr_id = '解析出的二维码ID' token = '移动端token'
response = requests.post('http://localhost:5000/scan_qr', json={'qr_id': qr_id, 'token': token}) temp_token = response.json()['temp_token']
response = requests.post('http://localhost:5000/confirm_login', json={'qr_id': qr_id, 'temp_token': temp_token, 'mobile_token': token}) login_token = response.json().get('login_token') if login_token: print("登录成功!") else: print("登录失败!")
|
这样一个简单的二维码登录的流程就出来了
案例解析
了解了流程之后我们来看看其他大型网站是如何实施的 这里拿哔哩哔哩来举例。
我们可以看到它的那个json实例
1 2 3 4 5 6 7 8 9 10 11 12
| { "code": 0, "message": "0", "ttl": 1, "data": { "url": "", "refresh_token": "", "timestamp": 0, "code": 86101, "message": "未扫码" } }
|
我们可以发现b站是不断的去发送这个请求,去轮询得到最新的扫码状态,请求间隔大约1s

之后当我们扫描后发现已经变成等待确认

当我们确认后,会返回

和我们说的流程大概的相同。
PS: 移动端和服务端确认登录后,服务端怎么通知到PC端,并发送token过去,这个实现细节其实一直比较好奇,原来是由前端一直轮询实现的。 :)