新增二维码生成接口及工具类
This commit is contained in:
188
docs/QR_CODE_API_USAGE.md
Normal file
188
docs/QR_CODE_API_USAGE.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# QR码API使用说明
|
||||
|
||||
## 概述
|
||||
|
||||
QR码API已经升级为接收JSON格式的请求数据,提供更好的类型安全和扩展性。
|
||||
|
||||
## API接口说明
|
||||
|
||||
### 1. 生成加密二维码
|
||||
|
||||
**接口地址:** `POST /api/qr-code/create-encrypted-qr-code`
|
||||
|
||||
**请求格式:** JSON
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"data": "用户ID:12345",
|
||||
"width": 200,
|
||||
"height": 200,
|
||||
"expireMinutes": 30,
|
||||
"businessType": "LOGIN"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `data` (必填): 要加密的数据
|
||||
- `width` (可选): 二维码宽度,默认200,范围50-1000
|
||||
- `height` (可选): 二维码高度,默认200,范围50-1000
|
||||
- `expireMinutes` (可选): 过期时间(分钟),默认30,范围1-1440
|
||||
- `businessType` (可选): 业务类型
|
||||
|
||||
### 2. 解密二维码数据
|
||||
|
||||
**接口地址:** `POST /api/qr-code/decrypt-qr-data`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"token": "abc123def456",
|
||||
"encryptedData": "encrypted_data_string"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 验证并解密二维码内容
|
||||
|
||||
**接口地址:** `POST /api/qr-code/verify-and-decrypt-qr`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"qrContent": "qr_content_string"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 验证并解密二维码内容(返回完整结果)
|
||||
|
||||
**接口地址:** `POST /api/qr-code/verify-and-decrypt-qr-with-type`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"qrContent": "qr_content_string"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 生成业务加密二维码
|
||||
|
||||
**接口地址:** `POST /api/qr-code/create-business-encrypted-qr-code`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"data": "订单ID:ORDER123",
|
||||
"businessKey": "store_key_123",
|
||||
"width": 200,
|
||||
"height": 200,
|
||||
"expireMinutes": 30,
|
||||
"businessType": "ORDER"
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 门店核销二维码
|
||||
|
||||
**接口地址:** `POST /api/qr-code/verify-business-qr`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"qrContent": "qr_content_string",
|
||||
"businessKey": "store_key_123"
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 使token失效
|
||||
|
||||
**接口地址:** `POST /api/qr-code/invalidate-token`
|
||||
|
||||
**请求示例:**
|
||||
```json
|
||||
{
|
||||
"token": "abc123def456"
|
||||
}
|
||||
```
|
||||
|
||||
## GET接口(保持不变)
|
||||
|
||||
以下GET接口保持原有的@RequestParam方式:
|
||||
|
||||
### 生成普通二维码
|
||||
`GET /api/qr-code/create-qr-code?data=要编码的数据&size=200x200`
|
||||
|
||||
### 生成加密二维码图片流
|
||||
`GET /api/qr-code/create-encrypted-qr-image?data=要加密的数据&size=200x200&expireMinutes=30&businessType=LOGIN`
|
||||
|
||||
**参数说明:**
|
||||
- `data` (必填): 要加密的数据
|
||||
- `size` (可选): 二维码尺寸,格式:宽x高,默认200x200
|
||||
- `expireMinutes` (可选): 过期时间(分钟),默认30
|
||||
- `businessType` (可选): 业务类型,如LOGIN、ORDER等
|
||||
|
||||
### 检查token是否有效
|
||||
`GET /api/qr-code/check-token?token=abc123def456`
|
||||
|
||||
## 错误处理
|
||||
|
||||
API现在包含完整的参数验证,会返回具体的错误信息:
|
||||
|
||||
**验证失败示例:**
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"message": "数据不能为空",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**常见验证错误:**
|
||||
- "数据不能为空"
|
||||
- "宽度不能小于50像素"
|
||||
- "宽度不能大于1000像素"
|
||||
- "过期时间不能小于1分钟"
|
||||
- "过期时间不能大于1440分钟"
|
||||
- "token不能为空"
|
||||
- "业务密钥不能为空"
|
||||
|
||||
## 前端调用示例
|
||||
|
||||
### JavaScript/Axios
|
||||
```javascript
|
||||
// 生成加密二维码
|
||||
const response = await axios.post('/api/qr-code/create-encrypted-qr-code', {
|
||||
data: '用户ID:12345',
|
||||
width: 200,
|
||||
height: 200,
|
||||
expireMinutes: 30,
|
||||
businessType: 'LOGIN'
|
||||
});
|
||||
|
||||
console.log(response.data);
|
||||
```
|
||||
|
||||
### jQuery
|
||||
```javascript
|
||||
$.ajax({
|
||||
url: '/api/qr-code/create-encrypted-qr-code',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
data: '用户ID:12345',
|
||||
width: 200,
|
||||
height: 200,
|
||||
expireMinutes: 30,
|
||||
businessType: 'LOGIN'
|
||||
}),
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 升级说明
|
||||
|
||||
1. **向下兼容性:** GET接口保持不变,现有的GET请求不受影响
|
||||
2. **类型安全:** JSON格式提供更好的类型检查和验证
|
||||
3. **扩展性:** 新的DTO结构便于后续添加新字段
|
||||
4. **错误处理:** 提供更详细和友好的错误信息
|
||||
5. **功能增强:** `create-encrypted-qr-image`接口现在支持`businessType`参数
|
||||
306
docs/QrCode_BusinessType_Usage.md
Normal file
306
docs/QrCode_BusinessType_Usage.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# 二维码业务类型使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
现在二维码系统支持业务类型(businessType)参数,允许前端在生成二维码时指定业务类型,解密后可以根据不同的业务类型进行相应的处理。
|
||||
|
||||
## 支持的业务类型示例
|
||||
|
||||
```javascript
|
||||
// 常见的业务类型
|
||||
const BUSINESS_TYPES = {
|
||||
ORDER: 'order', // 订单二维码
|
||||
USER: 'user', // 用户信息二维码
|
||||
COUPON: 'coupon', // 优惠券二维码
|
||||
GIFT: 'gitf', // 礼品卡二维码
|
||||
TICKET: 'ticket', // 门票二维码
|
||||
PAYMENT: 'payment', // 支付二维码
|
||||
CHECKIN: 'checkin', // 签到二维码
|
||||
PRODUCT: 'product', // 商品二维码
|
||||
MEMBER: 'member', // 会员卡二维码
|
||||
PARKING: 'parking', // 停车二维码
|
||||
ACCESS: 'access' // 门禁二维码
|
||||
};
|
||||
```
|
||||
|
||||
## API 接口使用
|
||||
|
||||
### 1. 生成带业务类型的加密二维码
|
||||
|
||||
```http
|
||||
POST /api/qr-code/create-encrypted-qr-code
|
||||
```
|
||||
|
||||
**参数:**
|
||||
```javascript
|
||||
{
|
||||
data: "order_id:12345,amount:88.50",
|
||||
width: 300,
|
||||
height: 300,
|
||||
expireMinutes: 60,
|
||||
businessType: "order" // 新增的业务类型参数
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "生成加密二维码成功",
|
||||
"data": {
|
||||
"qrCodeBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
|
||||
"token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
|
||||
"originalData": "order_id:12345,amount:88.50",
|
||||
"expireMinutes": "60",
|
||||
"businessType": "order"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 解密二维码(返回完整结果)
|
||||
|
||||
```http
|
||||
POST /api/qr-code/verify-and-decrypt-qr-with-type
|
||||
```
|
||||
|
||||
**参数:**
|
||||
```javascript
|
||||
{
|
||||
qrContent: '{"token":"...","data":"...","type":"encrypted","businessType":"order"}'
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "验证和解密成功",
|
||||
"data": {
|
||||
"originalData": "order_id:12345,amount:88.50",
|
||||
"businessType": "order",
|
||||
"qrType": "encrypted",
|
||||
"qrId": null,
|
||||
"expireTime": null,
|
||||
"expired": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 生成业务模式二维码
|
||||
|
||||
```http
|
||||
POST /api/qr-code/create-business-encrypted-qr-code
|
||||
```
|
||||
|
||||
**参数:**
|
||||
```javascript
|
||||
{
|
||||
data: "coupon_id:C001,discount:20%",
|
||||
businessKey: "store_001_secret",
|
||||
width: 250,
|
||||
height: 250,
|
||||
expireMinutes: 120,
|
||||
businessType: "coupon"
|
||||
}
|
||||
```
|
||||
|
||||
## 前端使用示例
|
||||
|
||||
### 场景1:餐厅点餐系统
|
||||
|
||||
```javascript
|
||||
// 1. 生成订单二维码
|
||||
async function generateOrderQrCode(orderData) {
|
||||
const response = await fetch('/api/qr-code/create-encrypted-qr-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({
|
||||
data: JSON.stringify(orderData),
|
||||
businessType: 'order',
|
||||
expireMinutes: 30
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
// 2. 扫码处理订单
|
||||
async function handleQrCodeScan(qrContent) {
|
||||
const response = await fetch('/api/qr-code/verify-and-decrypt-qr-with-type', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ qrContent })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
const { originalData, businessType } = result.data;
|
||||
|
||||
// 根据业务类型处理不同逻辑
|
||||
switch (businessType) {
|
||||
case 'order':
|
||||
handleOrderQrCode(originalData);
|
||||
break;
|
||||
case 'coupon':
|
||||
handleCouponQrCode(originalData);
|
||||
break;
|
||||
case 'user':
|
||||
handleUserQrCode(originalData);
|
||||
break;
|
||||
default:
|
||||
console.log('未知的业务类型:', businessType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 处理订单二维码
|
||||
function handleOrderQrCode(orderData) {
|
||||
const order = JSON.parse(orderData);
|
||||
console.log('处理订单:', order);
|
||||
|
||||
// 跳转到订单详情页
|
||||
window.location.href = `/order/detail?id=${order.orderId}`;
|
||||
}
|
||||
|
||||
// 4. 处理优惠券二维码
|
||||
function handleCouponQrCode(couponData) {
|
||||
const coupon = JSON.parse(couponData);
|
||||
console.log('处理优惠券:', coupon);
|
||||
|
||||
// 显示优惠券使用界面
|
||||
showCouponDialog(coupon);
|
||||
}
|
||||
```
|
||||
|
||||
### 场景2:会员系统
|
||||
|
||||
```javascript
|
||||
// 生成会员卡二维码
|
||||
async function generateMemberQrCode(memberId) {
|
||||
const memberData = {
|
||||
memberId: memberId,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
const qrResult = await generateQrCode({
|
||||
data: JSON.stringify(memberData),
|
||||
businessType: 'member',
|
||||
expireMinutes: 1440 // 24小时
|
||||
});
|
||||
|
||||
return qrResult;
|
||||
}
|
||||
|
||||
// 扫码验证会员
|
||||
async function verifyMemberQrCode(qrContent) {
|
||||
const result = await verifyQrCode(qrContent);
|
||||
|
||||
if (result.businessType === 'member') {
|
||||
const memberData = JSON.parse(result.originalData);
|
||||
|
||||
// 验证会员身份
|
||||
const member = await getMemberInfo(memberData.memberId);
|
||||
if (member) {
|
||||
showMemberInfo(member);
|
||||
} else {
|
||||
showError('会员不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 场景3:门店核销系统
|
||||
|
||||
```javascript
|
||||
// 门店核销优惠券
|
||||
async function verifyStoreCoupon(qrContent, storeKey) {
|
||||
const response = await fetch('/api/qr-code/verify-business-qr', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({
|
||||
qrContent: qrContent,
|
||||
businessKey: storeKey
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
// 解析二维码内容获取业务类型
|
||||
const qrData = JSON.parse(qrContent);
|
||||
const businessType = qrData.businessType;
|
||||
|
||||
switch (businessType) {
|
||||
case 'coupon':
|
||||
handleCouponVerification(result.data);
|
||||
break;
|
||||
case 'ticket':
|
||||
handleTicketVerification(result.data);
|
||||
break;
|
||||
default:
|
||||
console.log('门店不支持此类型的二维码:', businessType);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 业务类型的好处
|
||||
|
||||
### 1. **前端路由分发**
|
||||
```javascript
|
||||
function routeByBusinessType(businessType, data) {
|
||||
const routes = {
|
||||
'order': '/order/scan',
|
||||
'coupon': '/coupon/verify',
|
||||
'user': '/user/profile',
|
||||
'ticket': '/ticket/check',
|
||||
'payment': '/payment/process'
|
||||
};
|
||||
|
||||
const route = routes[businessType];
|
||||
if (route) {
|
||||
window.location.href = `${route}?data=${encodeURIComponent(data)}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **权限控制**
|
||||
```javascript
|
||||
function checkPermission(businessType, userRole) {
|
||||
const permissions = {
|
||||
'order': ['waiter', 'manager'],
|
||||
'coupon': ['cashier', 'manager'],
|
||||
'ticket': ['security', 'manager'],
|
||||
'payment': ['cashier', 'manager']
|
||||
};
|
||||
|
||||
return permissions[businessType]?.includes(userRole);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **统计分析**
|
||||
```javascript
|
||||
function trackQrCodeUsage(businessType, action) {
|
||||
analytics.track('qr_code_scan', {
|
||||
business_type: businessType,
|
||||
action: action,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **业务类型验证**:前端应该验证业务类型是否符合预期
|
||||
2. **权限检查**:根据业务类型检查用户是否有相应权限
|
||||
3. **错误处理**:优雅处理未知的业务类型
|
||||
4. **安全性**:业务类型不应包含敏感信息
|
||||
5. **向后兼容**:支持没有业务类型的旧二维码
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **统一业务类型常量**:在前后端定义统一的业务类型常量
|
||||
2. **类型验证**:在解密后验证业务类型是否符合当前场景
|
||||
3. **日志记录**:记录不同业务类型的使用情况
|
||||
4. **监控告警**:监控异常的业务类型使用
|
||||
5. **文档维护**:及时更新业务类型的文档说明
|
||||
215
docs/QrCode_Encryption_Usage.md
Normal file
215
docs/QrCode_Encryption_Usage.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# 加密二维码使用说明
|
||||
|
||||
## 概述
|
||||
|
||||
本系统提供了基于token的二维码加密和解密功能,可以安全地生成包含敏感信息的二维码,并设置过期时间。
|
||||
|
||||
## 主要特性
|
||||
|
||||
1. **AES加密**:使用AES对称加密算法保护二维码数据
|
||||
2. **Token机制**:每个二维码都有唯一的token作为密钥
|
||||
3. **过期控制**:可设置二维码的有效期(1-1440分钟)
|
||||
4. **Redis存储**:token和原始数据存储在Redis中,支持自动过期
|
||||
5. **数据验证**:解密时会验证数据完整性
|
||||
|
||||
## API接口
|
||||
|
||||
### 1. 生成普通二维码
|
||||
|
||||
```http
|
||||
GET /api/qr-code/create-qr-code?data=https://example.com&size=200x200
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `data`: 要编码的数据(必需)
|
||||
- `size`: 二维码尺寸,格式:宽x高 或 单个数字(可选,默认200x200)
|
||||
|
||||
**响应:** 直接返回PNG图片流
|
||||
|
||||
### 2. 生成加密二维码(返回JSON)
|
||||
|
||||
```http
|
||||
POST /api/qr-code/create-encrypted-qr-code
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `data`: 要加密的数据(必需)
|
||||
- `width`: 二维码宽度(可选,默认200)
|
||||
- `height`: 二维码高度(可选,默认200)
|
||||
- `expireMinutes`: 过期时间分钟数(可选,默认30,最大1440)
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "生成加密二维码成功",
|
||||
"data": {
|
||||
"qrCodeBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
|
||||
"token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
|
||||
"originalData": "https://example.com/user/123",
|
||||
"expireMinutes": "30"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 生成加密二维码(返回图片流)
|
||||
|
||||
```http
|
||||
GET /api/qr-code/create-encrypted-qr-image?data=https://example.com&size=300x300&expireMinutes=60
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `data`: 要加密的数据(必需)
|
||||
- `size`: 二维码尺寸(可选,默认200x200)
|
||||
- `expireMinutes`: 过期时间分钟数(可选,默认30)
|
||||
|
||||
**响应:** 直接返回PNG图片流,并在响应头中包含token信息
|
||||
- `X-QR-Token`: 二维码的token
|
||||
- `X-QR-Expire-Minutes`: 过期时间
|
||||
|
||||
### 4. 解密二维码数据
|
||||
|
||||
```http
|
||||
POST /api/qr-code/decrypt-qr-data
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `token`: token密钥(必需)
|
||||
- `encryptedData`: 加密的数据(必需)
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "解密成功",
|
||||
"data": "https://example.com/user/123"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 验证并解密二维码内容
|
||||
|
||||
```http
|
||||
POST /api/qr-code/verify-and-decrypt-qr
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `qrContent`: 二维码扫描得到的完整JSON内容(必需)
|
||||
|
||||
**二维码内容格式:**
|
||||
```json
|
||||
{
|
||||
"token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
|
||||
"data": "encrypted_data_here",
|
||||
"type": "encrypted"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "验证和解密成功",
|
||||
"data": "https://example.com/user/123"
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 检查token是否有效
|
||||
|
||||
```http
|
||||
GET /api/qr-code/check-token?token=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "检查完成",
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 使token失效
|
||||
|
||||
```http
|
||||
POST /api/qr-code/invalidate-token
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `token`: 要使失效的token(必需)
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "token已失效"
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 场景1:用户身份验证二维码
|
||||
|
||||
```javascript
|
||||
// 生成包含用户ID的加密二维码
|
||||
const response = await fetch('/api/qr-code/create-encrypted-qr-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'data=user_id:12345&expireMinutes=10'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
// 显示二维码图片:result.data.qrCodeBase64
|
||||
// 保存token用于后续验证:result.data.token
|
||||
```
|
||||
|
||||
### 场景2:临时访问链接
|
||||
|
||||
```javascript
|
||||
// 生成临时访问链接的二维码
|
||||
const accessUrl = 'https://example.com/temp-access?session=abc123';
|
||||
const response = await fetch('/api/qr-code/create-encrypted-qr-image?' +
|
||||
new URLSearchParams({
|
||||
data: accessUrl,
|
||||
size: '250x250',
|
||||
expireMinutes: '5'
|
||||
}));
|
||||
|
||||
// 直接使用返回的图片流
|
||||
// token信息在响应头 X-QR-Token 中
|
||||
```
|
||||
|
||||
### 场景3:扫码验证
|
||||
|
||||
```javascript
|
||||
// 扫描二维码后验证
|
||||
const qrContent = '{"token":"...","data":"...","type":"encrypted"}';
|
||||
const response = await fetch('/api/qr-code/verify-and-decrypt-qr', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'qrContent=' + encodeURIComponent(qrContent)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
console.log('原始数据:', result.data);
|
||||
} else {
|
||||
console.log('验证失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
1. **token保护**:token是解密的关键,应妥善保管
|
||||
2. **过期时间**:根据安全需求设置合适的过期时间
|
||||
3. **HTTPS传输**:生产环境中应使用HTTPS传输
|
||||
4. **访问控制**:可根据需要添加接口访问权限控制
|
||||
5. **日志记录**:建议记录二维码生成和验证的操作日志
|
||||
|
||||
## 错误处理
|
||||
|
||||
常见错误及处理:
|
||||
|
||||
- `token已过期或无效`:二维码已过期,需要重新生成
|
||||
- `数据验证失败`:加密数据被篡改或token不匹配
|
||||
- `尺寸必须在50-1000像素之间`:二维码尺寸超出允许范围
|
||||
- `过期时间必须在1-1440分钟之间`:过期时间设置不合理
|
||||
197
docs/QrCode_Two_Modes_Explanation.md
Normal file
197
docs/QrCode_Two_Modes_Explanation.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# 二维码加密的两种模式详解
|
||||
|
||||
## 问题背景
|
||||
|
||||
您提出的问题很关键:**用户生成二维码时的token和门店核销时的token是否一样?**
|
||||
|
||||
在实际业务场景中,这确实是个问题。我们提供了两种解决方案:
|
||||
|
||||
## 模式一:自包含模式(Self-Contained Mode)
|
||||
|
||||
### 特点
|
||||
- 二维码**包含所有解密所需的信息**
|
||||
- 扫码方**无需额外的密钥或token**
|
||||
- 适用于**点对点**的场景
|
||||
|
||||
### 工作流程
|
||||
```
|
||||
1. 用户生成二维码
|
||||
↓
|
||||
2. 系统生成随机token作为密钥
|
||||
↓
|
||||
3. 用token加密数据
|
||||
↓
|
||||
4. 二维码内容 = {token, 加密数据, 类型}
|
||||
↓
|
||||
5. 任何人扫码都能解密(因为token在二维码中)
|
||||
```
|
||||
|
||||
### 二维码内容示例
|
||||
```json
|
||||
{
|
||||
"token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
|
||||
"data": "encrypted_data_here",
|
||||
"type": "encrypted"
|
||||
}
|
||||
```
|
||||
|
||||
### 使用场景
|
||||
- 临时分享链接
|
||||
- 个人信息展示
|
||||
- 一次性验证码
|
||||
|
||||
### 安全性
|
||||
- ✅ 数据加密保护
|
||||
- ✅ 支持过期时间
|
||||
- ⚠️ 任何人扫码都能解密
|
||||
- ⚠️ 二维码泄露 = 数据泄露
|
||||
|
||||
---
|
||||
|
||||
## 模式二:业务模式(Business Mode)
|
||||
|
||||
### 特点
|
||||
- 使用**统一的业务密钥**
|
||||
- 门店有**预设的解密密钥**
|
||||
- 支持**防重复核销**
|
||||
- 适用于**商业核销**场景
|
||||
|
||||
### 工作流程
|
||||
```
|
||||
1. 用户生成二维码
|
||||
↓
|
||||
2. 使用预设的业务密钥(如门店密钥)加密
|
||||
↓
|
||||
3. 生成唯一的二维码ID
|
||||
↓
|
||||
4. 二维码内容 = {二维码ID, 加密数据, 类型}
|
||||
↓
|
||||
5. 门店用相同的业务密钥解密
|
||||
↓
|
||||
6. 系统标记该二维码为已使用
|
||||
```
|
||||
|
||||
### 二维码内容示例
|
||||
```json
|
||||
{
|
||||
"qrId": "abc123def456",
|
||||
"data": "encrypted_data_here",
|
||||
"type": "business_encrypted",
|
||||
"expire": "1692345678000"
|
||||
}
|
||||
```
|
||||
|
||||
### 密钥管理
|
||||
```
|
||||
门店A: businessKey = "store_001_secret_key"
|
||||
门店B: businessKey = "store_002_secret_key"
|
||||
门店C: businessKey = "store_003_secret_key"
|
||||
```
|
||||
|
||||
### 使用场景
|
||||
- 🎫 **优惠券核销**
|
||||
- 🍔 **餐厅点餐码**
|
||||
- 🎬 **电影票验证**
|
||||
- 🚗 **停车场进出**
|
||||
- 💊 **药品溯源**
|
||||
|
||||
### 安全性
|
||||
- ✅ 数据加密保护
|
||||
- ✅ 防重复核销
|
||||
- ✅ 门店权限控制
|
||||
- ✅ 即使二维码泄露,没有密钥也无法解密
|
||||
|
||||
---
|
||||
|
||||
## 实际应用示例
|
||||
|
||||
### 场景:餐厅点餐系统
|
||||
|
||||
#### 1. 用户下单生成二维码
|
||||
```java
|
||||
// 用户订单信息
|
||||
String orderData = "orderId:12345,tableNo:8,amount:88.50";
|
||||
|
||||
// 使用餐厅的业务密钥
|
||||
String restaurantKey = "restaurant_001_secret";
|
||||
|
||||
// 生成业务加密二维码
|
||||
Map<String, Object> result = encryptedQrCodeUtil.generateBusinessEncryptedQrCode(
|
||||
orderData, 300, 300, restaurantKey, 60L
|
||||
);
|
||||
```
|
||||
|
||||
#### 2. 服务员扫码核销
|
||||
```java
|
||||
// 扫码得到的内容
|
||||
String qrContent = "{\"qrId\":\"abc123\",\"data\":\"encrypted...\",\"type\":\"business_encrypted\"}";
|
||||
|
||||
// 使用餐厅密钥解密
|
||||
String orderInfo = encryptedQrCodeUtil.verifyAndDecryptQrCodeWithBusinessKey(
|
||||
qrContent, "restaurant_001_secret"
|
||||
);
|
||||
|
||||
// 结果:orderId:12345,tableNo:8,amount:88.50
|
||||
```
|
||||
|
||||
#### 3. 防重复核销
|
||||
```java
|
||||
// 第二次扫同一个二维码
|
||||
try {
|
||||
String orderInfo = encryptedQrCodeUtil.verifyAndDecryptQrCodeWithBusinessKey(
|
||||
qrContent, "restaurant_001_secret"
|
||||
);
|
||||
} catch (RuntimeException e) {
|
||||
// 抛出异常:二维码已被使用
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API接口对比
|
||||
|
||||
### 自包含模式
|
||||
```http
|
||||
# 生成
|
||||
POST /api/qr-code/create-encrypted-qr-code
|
||||
data=user_info&width=200&height=200&expireMinutes=30
|
||||
|
||||
# 解密(任何人都可以)
|
||||
POST /api/qr-code/verify-and-decrypt-qr
|
||||
qrContent={"token":"...","data":"...","type":"encrypted"}
|
||||
```
|
||||
|
||||
### 业务模式
|
||||
```http
|
||||
# 生成
|
||||
POST /api/qr-code/create-business-encrypted-qr-code
|
||||
data=order_info&businessKey=store_001_key&width=200&height=200&expireMinutes=60
|
||||
|
||||
# 核销(需要对应的业务密钥)
|
||||
POST /api/qr-code/verify-business-qr
|
||||
qrContent={"qrId":"...","data":"...","type":"business_encrypted"}&businessKey=store_001_key
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 选择建议
|
||||
|
||||
| 场景 | 推荐模式 | 原因 |
|
||||
|------|----------|------|
|
||||
| 个人信息分享 | 自包含模式 | 简单方便,无需额外配置 |
|
||||
| 临时链接分享 | 自包含模式 | 接收方无需特殊权限 |
|
||||
| 商业核销 | 业务模式 | 安全性高,防重复使用 |
|
||||
| 门店验证 | 业务模式 | 权限控制,业务流程完整 |
|
||||
| 支付码 | 业务模式 | 安全要求高 |
|
||||
| 会员卡 | 业务模式 | 需要权限验证 |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**您的疑问是对的!** 在门店核销场景中:
|
||||
|
||||
1. **自包含模式**:token在二维码中,门店直接扫码即可解密
|
||||
2. **业务模式**:门店有预设的业务密钥,用户生成时用这个密钥加密
|
||||
|
||||
**推荐使用业务模式**,因为它更符合实际的商业应用需求,安全性更高,且支持防重复核销。
|
||||
Reference in New Issue
Block a user