如何通过接口实现动态二维码的定时刷新

如何通过接口实现动态二维码的定时刷新?

感觉本篇对你有帮助可以关注一下我的微信公众号(深入浅出谈java),会不定期更新知识和面试资料、技巧!!!

一、需求场景

在Web应用中,动态二维码常用于以下场景:

  1. 登录验证:微信扫码登录、APP扫码授权
  2. 支付场景:支付宝/微信支付码定时刷新
  3. 票务系统:电子票二维码防截屏盗用
  4. 会员系统:动态会员码累计积分

二、技术方案设计

1、 整体流程

三、前端部分

大致流程

1、请求后端接口(按照现有项目格式即可)

2、后端接口的返回类型定义(byte\[\])

3、前端接收时定义响应类型(重点: responseType: 'arraybuffer')

4、对后端返回数据进行转化:arrayBufferToBase64

5、对数据和标签进行绑定

具体实现

1、获取后端 二维码 图片接口

js 复制代码
	import {
		generateMemberCode
	} from '@/api/inter/member-code.js';

data() {
			return {
				codeImg: '',
				timer: null, // 添加定时器引用,
			}
		},
		onShow() {
			// 立即加载一次
			this.makeMemberCode();

			// 设置定时器(注意保存引用)
			this.timer = setInterval(() => {
				this.makeMemberCode();
			}, 30000);
		},
		onHide() {
			// 页面隐藏时清除定时器
			if (this.timer) {
				clearInterval(this.timer);
				this.timer = null;
			}
		},
        methods: {
			async makeMemberCode() {
				const memberId = 1; // 使用假数据中的 id
				const memberName = '张三'; // 使用假数据中的 nickname
				const params = {
					memberId,
					memberName
				};
				try {
                    // 调用后端接口(这里可改成符合你项目的请求方式)
					const response = await generateMemberCode(params);
					console.log('完整响应:', response);

					// 调试数据类型
					console.log('响应数据类型:', typeof response);

					// 处理二进制数据(适用于小程序)
					if (typeof response === 'object' && response instanceof ArrayBuffer) {
						const base64 = uni.arrayBufferToBase64(response);
						this.codeImg = `data:image/png;base64,${base64}`;
						console.log('新的 codeImg:', this.codeImg); // 调试输出
					} else if (typeof response.data === 'string') {
						this.codeImg = `data:image/png;base64,${response.data}`;
					}
					// 处理URL
					else if (response.data.imageUrl) {
						this.codeImg = response.data.imageUrl;
					}
				} catch (error) {
					console.error('请求失败:', error);
					uni.showToast({
						title: '会员码加载失败',
						icon: 'none'
					});
				}
			}
        }

上面部分用到的代码: member-code.js

js 复制代码
import { HttpClient } from '@/api/utils/request.js'; // 确保路径正确
import api from '../../config/api.js'; // 导入 API 配置

const http = new HttpClient(api.base);


/**
 * 获取后端二维码
 */
export async function generateMemberCode(params) {
    try {
        const response = await http.getbuffer('/api/wx/generate-qrcode',params);
        console.log('Response:', response);
        return response; // 返回响应数据
    } catch (error) {
        console.error('Error:', error);
        throw error; // 继续抛出错误以供上层处理
    }
}

请求接口工具类:request.js

js 复制代码
class HttpClient {
	constructor(baseURL) {
		this.baseURL = baseURL;
	}

async getbuffer(url, params = {}) {
		try {
			//await this.checkTokenAndNavigate(); // 在发送请求前检查 token
			const queryString = this.buildQueryString(params);
			const fullURL = `${this.baseURL}${url}${queryString}`;
	
			return new Promise((resolve, reject) => {
				uni.request({
					url: fullURL,
					method: 'GET',
					header: {
						'Content-Type': 'application/json',
						'X-Auth-Token': `${uni.getStorageSync('token')}` // 如果需要在 header 中发送 token
					},
					responseType: 'arraybuffer', // 指定响应类型为 arraybuffer
					success: (response) => {
						resolve(response.data);
					},
					fail: (error) => {
						console.error('GET request error:', error);
						reject(error);
					}
				});
			});
		} catch (error) {
			// 如果 checkTokenAndNavigate 抛出错误(例如没有 token),则这里处理错误
			return Promise.reject(error);
		}
	}
    buildQueryString(params) {
	  if (!params || Object.keys(params).length === 0) {
	    return '';
	  }
	  return '?' + Object.keys(params)
	    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
	    .join('&');
	}
}

export {
	HttpClient
};

接口地址Api 配置类:API.js

js 复制代码
// 生产环境
const prod = {
  base: "http://XXXXXX:8099",
};

// 开发环境
const dev = {
  base: "http://XXXXXXX:8099",
};

// 默认生产环境
let api = prod;

// 如果是开发环境
if (process.env.NODE_ENV === "development") {
  api = dev;
}

// 微信小程序和App的打包方式建议为生产环境,所以这块直接条件编译赋值()
// #ifdef MP-WEIXIN || APP-PLUS
// 这个直接使用的是 dev 地址
api = dev;
// #endif

export default {
  ...api,
};

2、模板进行渲染

vue 复制代码
	<view class="qrcode d-flex just-content-center align-items-center">
					<image :src="`${codeImg}`" style="width: 350rpx; height: 350rpx;"></image>
				</view>

四、后端部分

具体实现

1、添加依赖

xml 复制代码
<!-- ZXing Core and Java SE -->
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>core</artifactId>
        <version>3.4.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>javase</artifactId>
        <version>3.4.1</version>
    </dependency>

2、接口实现controller

java 复制代码
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.ByteArrayOutputStream;
import java.nio.file.FileSystems;
import java.util.HashMap;
import java.util.Map;

@RestController
public class QRCodeController {

    @GetMapping("/generate-qrcode")
    public ResponseEntity<byte[]> generateQRCode(
            @RequestParam String memberId,
            @RequestParam String memberName) throws WriterException {
        
        // 构建二维码内容(内容可根据自己业务动态修改)
        // 建议加上唯一标识,安全码、起始时间、状态等,保证二维码安全性
        String qrContent = "Member ID: " + memberId + ", Member Name: " + memberName;

        // 设置二维码参数
        int width = 300;
        int height = 300;
        String imageFormat = "PNG"; // 图片格式

        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");

        // 生成二维码
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(qrContent, BarcodeFormat.QR_CODE, width, height, hints);

        // 将二维码写入字节数组输出流
        ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, pngOutputStream);

        // 返回二维码图片
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        return new ResponseEntity<>(pngOutputStream.toByteArray(), headers, HttpStatus.OK);
    }
}

接口测试

GET http://localhost:8080/generate-qrcode?memberId=12345&memberName=JohnDoe

前端处理后端的数据时,在一定要指定相应类型:responseType: 'arraybuffer'

五、效果展示

六、安全性设计

风险 防御方案
二维码盗用 30秒短时效性 + 单次有效性验证
接口暴力请求 限流策略(如Guava RateLimiter)
中间人攻击 全站HTTPS + 数据签名
XSS攻击 前端输入过滤 + CSP安全策略

七、总结

关键技术栈

  • 前端定时器 + 二进制流处理
  • 后端二维码生成 + 状态管理
  • 高效的缓存策略

最佳实践建议

  1. 始终为二维码添加时效性和唯一性标识
  2. 敏感操作需二次确认(如扫码后的授权确认)
  3. 监控二维码使用率,优化生成策略

通过前后端协作,动态二维码既能提升用户体验,又能有效保障系统安全性。

最后文章有啥不对,欢迎大佬在评论区指点!!!

如果感觉对你有帮助就点赞推荐或者关注一下吧!!!

相关推荐
文慧的科技江湖2 天前
一文读懂光伏、储能,充电工作原理,是如何实现闭环的,其中微电网(ems)是什么;这个内容,就看懂了源网荷储。十五五规划里面最核心的能源管理内容
开源·springboot·储能·光伏
AI人工智能+电脑小能手2 天前
【大白话说Java面试题 第163题】【06_Spring篇】第23题:说说SpringBoot 自动配置原理?
java·自动配置·springboot·spi·条件注解
AI人工智能+电脑小能手2 天前
【大白话说Java面试题 第164题】【06_Spring篇】第24题:如何理解 SpringBoot 的 Starter?
java·自动配置·springboot·starter·自定义组件
极光代码工作室2 天前
基于SpringBoot的订单管理系统
java·springboot·web开发·后端开发
衍生星球5 天前
SpringBoot3 + Vue3 + 微信小程序如何学习,以及学哪些技术,组件
sql·微信小程序·uni-app·vue·springboot
她说..6 天前
Java 默认值设置方式
java·开发语言·后端·springboot
折哥的程序人生 · 物流技术专研7 天前
Java面试通关⑨:SpringBoot核心全集
自动配置·springboot·starter·校招·java面试·面试真题·内嵌容器
ShiXZ2138 天前
PDF-OCR文件识别篇(八):配置、运维与排错
java·运维·ocr·dubbo·springboot
ShiXZ2138 天前
PDF-OCR文件识别篇(七):数据入库
java·pdf·json·ocr·springboot
带刺的坐椅15 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·springboot·web·solon