springboot如何通過接口實(shí)現(xiàn)動(dòng)態(tài)二維碼的定時(shí)刷新
一、需求場景
在Web應(yīng)用中,動(dòng)態(tài)二維碼常用于以下場景:
- 登錄驗(yàn)證:微信掃碼登錄、APP掃碼授權(quán)
- 支付場景:支付寶/微信支付碼定時(shí)刷新
- 票務(wù)系統(tǒng):電子票二維碼防截屏盜用
- 會(huì)員系統(tǒng):動(dòng)態(tài)會(huì)員碼累計(jì)積分
二、技術(shù)方案設(shè)計(jì)
1、 整體流程
三、前端部分
大致流程
1、請求后端接口(按照現(xiàn)有項(xiàng)目格式即可)
2、后端接口的返回類型定義(byte[])
3、前端接收時(shí)定義響應(yīng)類型(重點(diǎn):responseType: 'arraybuffer')
4、對后端返回?cái)?shù)據(jù)進(jìn)行轉(zhuǎn)化:arrayBufferToBase64
5、對數(shù)據(jù)和標(biāo)簽進(jìn)行綁定
具體實(shí)現(xiàn)
1、獲取后端 二維碼 圖片接口
import { generateMemberCode } from '@/api/inter/member-code.js'; data() { return { codeImg: '', timer: null, // 添加定時(shí)器引用, } }, onShow() { // 立即加載一次 this.makeMemberCode(); // 設(shè)置定時(shí)器(注意保存引用) this.timer = setInterval(() => { this.makeMemberCode(); }, 30000); }, onHide() { // 頁面隱藏時(shí)清除定時(shí)器 if (this.timer) { clearInterval(this.timer); this.timer = null; } }, methods: { async makeMemberCode() { const memberId = 1; // 使用假數(shù)據(jù)中的 id const memberName = '張三'; // 使用假數(shù)據(jù)中的 nickname const params = { memberId, memberName }; try { // 調(diào)用后端接口(這里可改成符合你項(xiàng)目的請求方式) const response = await generateMemberCode(params); console.log('完整響應(yīng):', response); // 調(diào)試數(shù)據(jù)類型 console.log('響應(yīng)數(shù)據(jù)類型:', typeof response); // 處理二進(jìn)制數(shù)據(jù)(適用于小程序) if (typeof response === 'object' && response instanceof ArrayBuffer) { const base64 = uni.arrayBufferToBase64(response); this.codeImg = `data:image/png;base64,${base64}`; console.log('新的 codeImg:', this.codeImg); // 調(diào)試輸出 } 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: '會(huì)員碼加載失敗', icon: 'none' }); } } }
上面部分用到的代碼: member-code.js
import { HttpClient } from '@/api/utils/request.js'; // 確保路徑正確 import api from '../../config/api.js'; // 導(dǎo)入 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; // 返回響應(yīng)數(shù)據(jù) } catch (error) { console.error('Error:', error); throw error; // 繼續(xù)拋出錯(cuò)誤以供上層處理 } }
請求接口工具類:request.js
class HttpClient { constructor(baseURL) { this.baseURL = baseURL; } async getbuffer(url, params = {}) { try { //await this.checkTokenAndNavigate(); // 在發(fā)送請求前檢查 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 中發(fā)送 token }, responseType: 'arraybuffer', // 指定響應(yīng)類型為 arraybuffer success: (response) => { resolve(response.data); }, fail: (error) => { console.error('GET request error:', error); reject(error); } }); }); } catch (error) { // 如果 checkTokenAndNavigate 拋出錯(cuò)誤(例如沒有 token),則這里處理錯(cuò)誤 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
// 生產(chǎn)環(huán)境 const prod = { base: "http://XXXXXX:8099", }; // 開發(fā)環(huán)境 const dev = { base: "http://XXXXXXX:8099", }; // 默認(rèn)生產(chǎn)環(huán)境 let api = prod; // 如果是開發(fā)環(huán)境 if (process.env.NODE_ENV === "development") { api = dev; } // 微信小程序和App的打包方式建議為生產(chǎn)環(huán)境,所以這塊直接條件編譯賦值() // #ifdef MP-WEIXIN || APP-PLUS // 這個(gè)直接使用的是 dev 地址 api = dev; // #endif export default { ...api, };
2、模板進(jìn)行渲染
<view class="qrcode d-flex just-content-center align-items-center"> <image :src="`${codeImg}`" style="width: 350rpx; height: 350rpx;"></image> </view>
四、后端部分
具體實(shí)現(xiàn)
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、接口實(shí)現(xiàn)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 { // 構(gòu)建二維碼內(nèi)容(內(nèi)容可根據(jù)自己業(yè)務(wù)動(dòng)態(tài)修改) // 建議加上唯一標(biāo)識,安全碼、起始時(shí)間、狀態(tài)等,保證二維碼安全性 String qrContent = "Member ID: " + memberId + ", Member Name: " + memberName; // 設(shè)置二維碼參數(shù) 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); // 將二維碼寫入字節(jié)數(shù)組輸出流 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
前端處理后端的數(shù)據(jù)時(shí),在一定要指定相應(yīng)類型:responseType: 'arraybuffer'
五、效果展示
六、安全性設(shè)計(jì)
風(fēng)險(xiǎn) | 防御方案 |
---|---|
二維碼盜用 | 30秒短時(shí)效性 + 單次有效性驗(yàn)證 |
接口的暴力請求 | 限流策略(如Guava RateLimiter) |
中間人 攻擊 | 全站HTTPS + 數(shù)據(jù)簽名 |
XSS攻擊 | 前端輸入過濾 + CSP安全策略 |
七、總結(jié)
關(guān)鍵技術(shù)棧:
- 前端定時(shí)器 + 二進(jìn)制流處理
- 后端二維碼生成 + 狀態(tài)管理
- 高效的緩存策略
最佳實(shí)踐建議:
- 始終為二維碼添加時(shí)效性和唯一性標(biāo)識
- 敏感操作需二次確認(rèn)(如掃碼后的授權(quán)確認(rèn))
- 監(jiān)控二維碼使用率,優(yōu)化生成策略
通過前后端協(xié)作,動(dòng)態(tài)二維碼既能提升用戶體驗(yàn),又能有效保障系統(tǒng)安全性。
以上就是springboot如何通過接口實(shí)現(xiàn)動(dòng)態(tài)二維碼的定時(shí)刷新的詳細(xì)內(nèi)容,更多關(guān)于springboot動(dòng)態(tài)二維碼刷新的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot中的屬性綁定的實(shí)現(xiàn)
這篇文章主要介紹了Spring Boot中的屬性綁定的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04UniApp?+?SpringBoot?實(shí)現(xiàn)微信支付和退款功能
這篇文章主要介紹了UniApp?+?SpringBoot?實(shí)現(xiàn)微信支付和退款功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06Java中的ArrayList、LinkedList、HashSet等容器詳解
這篇文章主要介紹了Java中的ArrayList、LinkedList、HashSet等容器詳解,集合表示一組對象,稱為其元素,有些集合允許重復(fù)元素,而另一些則不允許,有些是有序的,有些是無序的,需要的朋友可以參考下2023-08-08一文帶你掌握J(rèn)ava?SPI的原理和實(shí)踐
在Java中,我們經(jīng)常會(huì)提到面向接口編程,這樣減少了模塊之間的耦合,更加靈活,Java?SPI?(Service?Provider?Interface)就提供了這樣的機(jī)制,本文就來講講它的原理與具體使用吧2023-05-05