扫码登录网页应用
实现扫码登录第三方网站。扫码登录指轻推客户端扫码并确认登录 web 系统,在系统内获取正在访问用户的轻推身份,无需输入账号密码的开发流程。
注:当前默认未开放,需联系轻推技术人员开通
开发流程概览
- 申请轻推应用
- 打开二维码
- 用户扫码确认
- 获取 access_token
- 刷新 token(可选)
- 获取用户信息
系统交互时序图
完整流程图
一、申请轻推应用
在开始开发前,需要向轻推申请应用,提供以下信息:
| 参数 | 说明 |
|---|---|
| 名称 | 应用的名称 |
| 头像图 | 应用的图标 |
| 安全域名 | 用于接收回调的域名,必须是有效的 HTTPS 域名 |
申请成功后,您将获得:
- client_id:应用的唯一标识,由轻推分配
- secret:应用的密钥,暂时由轻推分配(请妥善保管)
二、打开登录二维码
引导用户访问轻推授权页面,展示登录二维码。
二维码生成流程
请求地址: https://open.qingtui.cn/pages/oauth/qrcode
请求方式: GET
请求参数:
| 参数 | 必须 | 类型 | 说明 |
|---|---|---|---|
| client_id | 是 | string | 应用的 client_id,由轻推分配 |
| redirect_uri | 是 | string | 扫码确认后重定向的 URL,需要 URL Encoding。扫码成功后会跳转到此地址,URL 必须在安全域名以内 |
示例:
https://open.qingtui.cn/pages/oauth/qrcode?client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fyourdomain.com%2Fcallback
说明:
- 用户访问该地址后,会看到一个二维码页面
- 用户使用轻推客户端扫描二维码
- 用户在客户端确认授权登录
- 扫码确认后,浏览器会重定向到 redirect_uri,并附带 code 参数
授权状态流转图
三、获取 Access Token
用户扫码确认后,使用 code 换取 access_token。
Token 获取时序图
Token 状态流转
请求地址: https://open.qingtui.cn/oauth/token
请求方式: GET
请求参数:
| 参数 | 必须 | 类型 | 说明 |
|---|---|---|---|
| client_id | 是 | string | 应用的 client_id,由轻推分配 |
| secret | 是 | string | 应用的 secret,由轻推分配 |
| code | 是 | string | 扫码成功后重定向时携带的授权码 |
响应数据结构:
{
"access_token": "xxx",
"refresh_token": "xxx",
"open_id": "xxx"
}
参数说明:
| 参数 | 说明 |
|---|---|
| access_token | 访问令牌,用于调用其他接口 |
| refresh_token | 刷新令牌,用于刷新 access_token |
| open_id | 用户的唯一标识 |
示例代码:
// 假设 redirect_uri 回调后获取到 code
const code = 'authorization_code_from_callback';
fetch(`https://open.qingtui.cn/oauth/token?client_id=${CLIENT_ID}&secret=${SECRET}&code=${code}`)
.then(response => response.json())
.then(data => {
console.log('Access Token:', data.access_token);
console.log('Open ID:', data.open_id);
// 保存 token 和用户信息
})
.catch(error => console.error('Error:', error));
四、刷新 Access Token
access_token 可能过期,可以使用 refresh_token 刷新获取新的 access_token。
Token 刷新机制时序图
自动刷新策略流程图
请求地址: https://open.qingtui.cn/oauth/token/refresh
请求方式: GET
请求参数:
| 参数 | 必须 | 类型 | 说明 |
|---|---|---|---|
| refresh_token | 是 | string | 获取token响应体中的 refresh_token |
响应数据结构:
{
"access_token": "xxx",
"refresh_token": "xxx",
"open_id": "xxx"
}
说明:
- 建议在 access_token 即将过期前主动刷新
- 每次刷新会返回新的 refresh_token,请替换旧的 refresh_token
示例代码:
async function refreshAccessToken(refreshToken) {
try {
const response = await fetch(
`https://open.qingtui.cn/oauth/token/refresh?refresh_token=${refreshToken}`
);
const data = await response.json();
// 更新存储的 token
updateStorage({
access_token: data.access_token,
refresh_token: data.refresh_token,
open_id: data.open_id
});
return data;
} catch (error) {
console.error('刷新 token 失败:', error);
throw error;
}
}
五、获取用户信息
使用 access_token 获取当前登录用户的基本信息。
用户信息获取时序图
用户数据缓存策略
请求地址: https://open.qingtui.cn/oauth/user/info
请求方式: GET
请求参数:
| 参数 | 必须 | 类型 | 说明 |
|---|---|---|---|
| client_id | 是 | string | 应用的 client_id |
| open_id | 是 | string | 用户的 open_id |
| access_token | 是 | string | 有效的 access_token |
响应数据结构:
{
"name": "xxx",
"avatar": "xxx"
}
参数说明:
| 参数 | 说明 |
|---|---|
| name | 用户姓名 |
| avatar | 用户头像 URL |
示例代码:
async function getUserInfo(accessToken, openId) {
try {
const response = await fetch(
`https://open.qingtui.cn/oauth/user/info?client_id=${CLIENT_ID}&open_id=${openId}&access_token=${accessToken}`
);
const userInfo = await response.json();
console.log('用户姓名:', userInfo.name);
console.log('用户头像:', userInfo.avatar);
return userInfo;
} catch (error) {
console.error('获取用户信息失败:', error);
throw error;
}
}
完整登录流程示例
前端页面示例
<!DOCTYPE html>
<html>
<head>
<title>轻推扫码登录</title>
</head>
<body>
<div id="login-container">
<button onclick="showQrcode()">扫码登录</button>
</div>
<script>
const CLIENT_ID = 'your_client_id';
const REDIRECT_URI = encodeURIComponent('https://yourdomain.com/callback');
// 显示二维码
function showQrcode() {
const qrcodeUrl = `https://open.qingtui.cn/pages/oauth/qrcode?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}`;
window.location.href = qrcodeUrl;
}
</script>
</body>
</html>
后端回调处理示例(Node.js)
const express = require('express');
const axios = require('axios');
const app = express();
const CLIENT_ID = 'your_client_id';
const SECRET = 'your_secret';
// 处理扫码回调
app.get('/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.status(400).send('缺少授权码');
}
try {
// 1. 使用 code 换取 access_token
const tokenResponse = await axios.get(
`https://open.qingtui.cn/oauth/token`,
{
params: {
client_id: CLIENT_ID,
secret: SECRET,
code: code
}
}
);
const { access_token, refresh_token, open_id } = tokenResponse.data;
// 2. 获取用户信息
const userResponse = await axios.get(
`https://open.qingtui.cn/oauth/user/info`,
{
params: {
client_id: CLIENT_ID,
open_id: open_id,
access_token: access_token
}
}
);
const userInfo = userResponse.data;
// 3. 创建本地会话,保存用户信息
// ... 这里添加您的业务逻辑
// 4. 重定向到首页
res.redirect('/home');
} catch (error) {
console.error('登录失败:', error);
res.status(500).send('登录失败');
}
});
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
注意事项
-
安全性
- Secret 是关键信息,请妥善保管,不要泄露
- 所有涉及 token 的操作都应该在后端进行
- 确保使用 HTTPS 协议传输敏感信息
-
Token 管理
- access_token 有有效期,建议检测过期时间并及时刷新
- refresh_token 也需要安全存储
- 建议实现 token 自动刷新机制
-
错误处理
- 网络请求应该添加重试机制
- token 失效时应引导用户重新扫码登录
- 记录关键操作的日志便于排查问题
-
用户体验
- 提供清晰的登录指引
- 加载状态要有明确的提示
- 考虑移动端和 PC 端的适配
常见问题
Q: redirect_uri 有什么限制?
A: redirect_uri 必须在申请应用时填写的安全域名范围内,且需要进行 URL Encoding。
Q: access_token 有效期多久?
A: access_token 有一定的有效期,建议在过期前使用 refresh_token 刷新。
Q: 如何处理登录态过期?
A: 检测到 token 过期后,可以引导用户重新扫码,或者使用 refresh_token 刷新(如果还在有效期内)。
Q: 一个用户可以授权给多个应用吗?
A: 可以,同一个用户对不同的应用会有不同的 open_id。