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

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

感觉本篇对你有帮助可以关注一下我的微信公众号(深入浅出谈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. 监控二维码使用率,优化生成策略

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

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

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

相关推荐
abcnull11 小时前
java中自定义注解
java·spring·springboot·web·注解
编程、小哥哥4 天前
互联网大厂Java求职面试实录 —— 严肃面试官遇到搞笑水货程序员
java·面试·mybatis·dubbo·springboot·多线程·juc
麓殇⊙5 天前
黑马点评--短信登录实现
java·springboot
苏生Susheng5 天前
【工具类】常用的工具类——CollectionUtil
java·spring cloud·springboot·集合·软件开发·工具类·collectionutil
todoitbo7 天前
开源一个记账软件,支持docker一键部署
运维·docker·容器·开源·springboot·记账软件·容器部署
Code blocks7 天前
百度OCR:证件识别
java·ocr·springboot·web
白日依山尽yy7 天前
SpringBootDay1|面试题
springboot
孔明click338 天前
Sa-Token v1.43.0 发布 🚀,新增 SSO 单设备注销、消息推送,多 Access-Token 并存能力
java·sa-token·springboot·登录·权限认证
菠萝崽.10 天前
Elasticsearch进阶篇-DSL
大数据·分布式·elasticsearch·搜索引擎·全文检索·jenkins·springboot