找回密码
 立即注册
首页 业界区 业界 如何通过接口实现动态二维码的定时刷新 ...

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

各卧唯 前天 10:21
如何通过接口实现动态二维码的定时刷新?

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

一、需求场景

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

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

1、 整体流程
2.png

三、前端部分

大致流程

1、请求后端接口(按照现有项目格式即可)
2、后端接口的返回类型定义(byte[])
3、前端接收时定义响应类型(重点:responseType: 'arraybuffer')
4、对后端返回数据进行转化:arrayBufferToBase64
5、对数据和标签进行绑定
具体实现

1、获取后端 二维码 图片接口
  1.         import {
  2.                 generateMemberCode
  3.         } from '@/api/inter/member-code.js';
  4. data() {
  5.                         return {
  6.                                 codeImg: '',
  7.                                 timer: null, // 添加定时器引用,
  8.                         }
  9.                 },
  10.                 onShow() {
  11.                         // 立即加载一次
  12.                         this.makeMemberCode();
  13.                         // 设置定时器(注意保存引用)
  14.                         this.timer = setInterval(() => {
  15.                                 this.makeMemberCode();
  16.                         }, 30000);
  17.                 },
  18.                 onHide() {
  19.                         // 页面隐藏时清除定时器
  20.                         if (this.timer) {
  21.                                 clearInterval(this.timer);
  22.                                 this.timer = null;
  23.                         }
  24.                 },
  25.         methods: {
  26.                         async makeMemberCode() {
  27.                                 const memberId = 1; // 使用假数据中的 id
  28.                                 const memberName = '张三'; // 使用假数据中的 nickname
  29.                                 const params = {
  30.                                         memberId,
  31.                                         memberName
  32.                                 };
  33.                                 try {
  34.                     // 调用后端接口(这里可改成符合你项目的请求方式)
  35.                                         const response = await generateMemberCode(params);
  36.                                         console.log('完整响应:', response);
  37.                                         // 调试数据类型
  38.                                         console.log('响应数据类型:', typeof response);
  39.                                         // 处理二进制数据(适用于小程序)
  40.                                         if (typeof response === 'object' && response instanceof ArrayBuffer) {
  41.                                                 const base64 = uni.arrayBufferToBase64(response);
  42.                                                 this.codeImg = `data:image/png;base64,${base64}`;
  43.                                                 console.log('新的 codeImg:', this.codeImg); // 调试输出
  44.                                         } else if (typeof response.data === 'string') {
  45.                                                 this.codeImg = `data:image/png;base64,${response.data}`;
  46.                                         }
  47.                                         // 处理URL
  48.                                         else if (response.data.imageUrl) {
  49.                                                 this.codeImg = response.data.imageUrl;
  50.                                         }
  51.                                 } catch (error) {
  52.                                         console.error('请求失败:', error);
  53.                                         uni.showToast({
  54.                                                 title: '会员码加载失败',
  55.                                                 icon: 'none'
  56.                                         });
  57.                                 }
  58.                         }
  59.         }
复制代码
上面部分用到的代码:  member-code.js
  1. import { HttpClient } from '@/api/utils/request.js'; // 确保路径正确
  2. import api from '../../config/api.js'; // 导入 API 配置
  3. const http = new HttpClient(api.base);
  4. /**
  5. * 获取后端二维码
  6. */
  7. export async function generateMemberCode(params) {
  8.     try {
  9.         const response = await http.getbuffer('/api/wx/generate-qrcode',params);
  10.         console.log('Response:', response);
  11.         return response; // 返回响应数据
  12.     } catch (error) {
  13.         console.error('Error:', error);
  14.         throw error; // 继续抛出错误以供上层处理
  15.     }
  16. }
复制代码
请求接口工具类:request.js
  1. class HttpClient {
  2.         constructor(baseURL) {
  3.                 this.baseURL = baseURL;
  4.         }
  5. async getbuffer(url, params = {}) {
  6.                 try {
  7.                         //await this.checkTokenAndNavigate(); // 在发送请求前检查 token
  8.                         const queryString = this.buildQueryString(params);
  9.                         const fullURL = `${this.baseURL}${url}${queryString}`;
  10.        
  11.                         return new Promise((resolve, reject) => {
  12.                                 uni.request({
  13.                                         url: fullURL,
  14.                                         method: 'GET',
  15.                                         header: {
  16.                                                 'Content-Type': 'application/json',
  17.                                                 'X-Auth-Token': `${uni.getStorageSync('token')}` // 如果需要在 header 中发送 token
  18.                                         },
  19.                                         responseType: 'arraybuffer', // 指定响应类型为 arraybuffer
  20.                                         success: (response) => {
  21.                                                 resolve(response.data);
  22.                                         },
  23.                                         fail: (error) => {
  24.                                                 console.error('GET request error:', error);
  25.                                                 reject(error);
  26.                                         }
  27.                                 });
  28.                         });
  29.                 } catch (error) {
  30.                         // 如果 checkTokenAndNavigate 抛出错误(例如没有 token),则这里处理错误
  31.                         return Promise.reject(error);
  32.                 }
  33.         }
  34.     buildQueryString(params) {
  35.           if (!params || Object.keys(params).length === 0) {
  36.             return '';
  37.           }
  38.           return '?' + Object.keys(params)
  39.             .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
  40.             .join('&');
  41.         }
  42. }
  43. export {
  44.         HttpClient
  45. };
复制代码
接口地址Api 配置类:API.js
  1. // 生产环境
  2. const prod = {
  3.   base: "http://XXXXXX:8099",
  4. };
  5. // 开发环境
  6. const dev = {
  7.   base: "http://XXXXXXX:8099",
  8. };
  9. // 默认生产环境
  10. let api = prod;
  11. // 如果是开发环境
  12. if (process.env.NODE_ENV === "development") {
  13.   api = dev;
  14. }
  15. // 微信小程序和App的打包方式建议为生产环境,所以这块直接条件编译赋值()
  16. // #ifdef MP-WEIXIN || APP-PLUS
  17. // 这个直接使用的是 dev 地址
  18. api = dev;
  19. // #endif
  20. export default {
  21.   ...api,
  22. };
复制代码
2、模板进行渲染
  1.         <view >
  2.                                         <image :src="`${codeImg}`" ></image>
  3.                                 </view>
复制代码
四、后端部分

具体实现

1、添加依赖
  1.     <dependency>
  2.         <groupId>com.google.zxing</groupId>
  3.         core</artifactId>
  4.         <version>3.4.1</version>
  5.     </dependency>
  6.     <dependency>
  7.         <groupId>com.google.zxing</groupId>
  8.         javase</artifactId>
  9.         <version>3.4.1</version>
  10.     </dependency>
复制代码
2、接口实现controller
  1. import com.google.zxing.BarcodeFormat;
  2. import com.google.zxing.EncodeHintType;
  3. import com.google.zxing.WriterException;
  4. import com.google.zxing.client.j2se.MatrixToImageWriter;
  5. import com.google.zxing.common.BitMatrix;
  6. import com.google.zxing.qrcode.QRCodeWriter;
  7. import org.springframework.http.HttpHeaders;
  8. import org.springframework.http.HttpStatus;
  9. import org.springframework.http.MediaType;
  10. import org.springframework.http.ResponseEntity;
  11. import org.springframework.web.bind.annotation.GetMapping;
  12. import org.springframework.web.bind.annotation.RequestParam;
  13. import org.springframework.web.bind.annotation.RestController;
  14. import java.io.ByteArrayOutputStream;
  15. import java.nio.file.FileSystems;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. @RestController
  19. public class QRCodeController {
  20.     @GetMapping("/generate-qrcode")
  21.     public ResponseEntity<byte[]> generateQRCode(
  22.             @RequestParam String memberId,
  23.             @RequestParam String memberName) throws WriterException {
  24.         
  25.         // 构建二维码内容(内容可根据自己业务动态修改)
  26.         // 建议加上唯一标识,安全码、起始时间、状态等,保证二维码安全性
  27.         String qrContent = "Member ID: " + memberId + ", Member Name: " + memberName;
  28.         // 设置二维码参数
  29.         int width = 300;
  30.         int height = 300;
  31.         String imageFormat = "PNG"; // 图片格式
  32.         Map<EncodeHintType, Object> hints = new HashMap<>();
  33.         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
  34.         // 生成二维码
  35.         QRCodeWriter qrCodeWriter = new QRCodeWriter();
  36.         BitMatrix bitMatrix = qrCodeWriter.encode(qrContent, BarcodeFormat.QR_CODE, width, height, hints);
  37.         // 将二维码写入字节数组输出流
  38.         ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
  39.         MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, pngOutputStream);
  40.         // 返回二维码图片
  41.         HttpHeaders headers = new HttpHeaders();
  42.         headers.setContentType(MediaType.IMAGE_PNG);
  43.         return new ResponseEntity<>(pngOutputStream.toByteArray(), headers, HttpStatus.OK);
  44.     }
  45. }
复制代码
接口测试
GET http://localhost:8080/generate-qrcode?memberId=12345&memberName=JohnDoe
3.png

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

五、效果展示

5.gif

六、安全性设计

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

关键技术栈

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

  • 始终为二维码添加时效性和唯一性标识
  • 敏感操作需二次确认(如扫码后的授权确认)
  • 监控二维码使用率,优化生成策略
通过前后端协作,动态二维码既能提升用户体验,又能有效保障系统安全性。
最后文章有啥不对,欢迎大佬在评论区指点!!!
如果感觉对你有帮助就点赞推荐或者关注一下吧!!!
6.gif


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册