开发者

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

目录
  • 一、需求场景
  • 二、技术方案设计
  • 三、前端部分
    • 大致流程
    • 具体实现
  • 四、后端部分
    • 五、效果展示
      • 六、安全性设计
        • 七、总结

          一、需求场景

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

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

          二、技术方案设计

          1、 整体流程

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

          三、前端部分

          大致流程

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

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

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

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

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

          具体实现

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

          	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

          import { HttpClient } from '@/api/utils/request.js'; // 确保路径正确
          import api from '../../config/api.js'; // 导入 API 配置
          
          const http = new HttpClient(api.base);
          
          
          /**
           * 获取后端二维码
           */
          expjavascriptort 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

          class HttpClient {
          	constructor(baseURL) {
          		this.baseURL = baseURL;
          	}
          
          async getbuffer(url, params = {}) {
          		try {
          			//await this.checkTokenAndNavigate(); // 在发送请求前检查 tokehttp://www.devze.comn
          			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

          // 生产环境
          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 {
            .android..api,
          };
          

          2、模板进行渲染

          	<view class="qrcode d-Flex just-content-center align-items-center">
          					<image :src="`${codeImg}`"></image>
          			http://www.devze.com	</view>
          

          四、后端部分

          具体实现

          1、添加依赖

          <!-- 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

          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, imageFor编程客栈mat, 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

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

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

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

          五、效果展示

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

          六、安全性设计

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

          七、总结

          关键技术栈

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

          最佳实践建议

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

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

          以上就是springboot如何通过接口实现动态二维码的定时刷新的详细内容,更多关于springboot动态二维码刷新的资料请关注编程客栈(www.devze.com)其它相关文章!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜