Django集成BankID生成HMAC动态QR码报错及前端渲染问题
问题1:BankID官方示例代码运行报错解决方案
报错是因为官方提供的示例为Python2语法,Python3中hmac.new方法要求密钥和待哈希内容都必须是字节类型,不能直接传入字符串,同时示例中的str.join写法也不符合Python3规范。
修正后的核心可运行代码如下:
import hashlib import hmac import time qr_start_token = rp_response["qrStartToken"] qr_start_secret = rp_response["qrStartSecret"] order_time = time.time() # 所有字符串统一转utf-8字节后传入hmac方法 qr_time = str(int(time.time() - order_time)) qr_auth_code = hmac.new( qr_start_secret.encode('utf-8'), qr_time.encode('utf-8'), hashlib.sha256 ).hexdigest() # 修正join的写法,用点调用列表的join方法 qr_data = ".".join(["bankid", qr_start_token, qr_time, qr_auth_code])
以上代码可以直接解决你遇到的两类类型报错。
问题2:Django端实现动态刷新BankID二维码方案
你当前写的while循环存在严重问题:同步Django视图中写死循环会直接阻塞请求,浏览器会一直处于等待响应状态,根本无法加载页面,而且将二维码写入本地磁盘的逻辑在多用户场景下会出现文件覆盖冲突,性能也极低。
正确的实现逻辑拆分如下:
第一步:调整视图初始化逻辑
不要在视图中等待验证完成,拿到BankID返回的orderRef、qrStartToken、qrStartSecret以及当前时间戳后,直接存入用户session,先返回包含二维码容器的前端页面:
# 初始化请求BankID后的视图逻辑 if user_agent.is_pc: auth = client.auth(...) # 保留你原有调用BankID获取授权信息的逻辑 # 核心参数存入session request.session['bankid_order_ref'] = auth["orderRef"] request.session['bankid_qr_start_token'] = auth["qrStartToken"] request.session['bankid_qr_start_secret'] = auth["qrStartSecret"] request.session['bankid_order_time'] = time.time() # 直接返回页面,不等待验证循环结束 return render(request, 'home/qr_login.html')
第二步:新增后端接口
编写两个单独的接口,分别用于获取最新二维码数据、查询验证状态:
# 获取最新qr_data的接口 def get_latest_qr(request): order_time = request.session.get('bankid_order_time') qr_start_token = request.session.get('bankid_qr_start_token') qr_start_secret = request.session.get('bankid_qr_start_secret') if not all([order_time, qr_start_token, qr_start_secret]): return JsonResponse({'status': 'invalid'}) qr_time = str(int(time.time() - order_time)) qr_auth_code = hmac.new( qr_start_secret.encode('utf-8'), qr_time.encode('utf-8'), hashlib.sha256 ).hexdigest() qr_data = ".".join(["bankid", qr_start_token, qr_time, qr_auth_code]) return JsonResponse({'qr_data': qr_data}) # 查询验证状态的接口 def check_bankid_status(request): order_ref = request.session.get('bankid_order_ref') if not order_ref: return JsonResponse({'status': 'invalid'}) status = client.collect(order_ref=order_ref)["status"] if status == 'complete': # 保留你原有验证成功的逻辑,比如登录用户,清理session里的BankID参数 dj_login(request, user) # 清理临时session参数 for key in ['bankid_order_ref', 'bankid_qr_start_token', 'bankid_qr_start_secret', 'bankid_order_time']: del request.session[key] return JsonResponse({'status': status})
第三步:前端实现定时刷新
在qr_login.html中引入二维码生成JS库,编写定时器每秒请求后端接口更新二维码、查询状态:
<!-- 页面上的二维码容器 --> <div id="qrcode-container"></div> <script src="https://cdn.staticfile.org/qrcodejs/1.0.0/qrcode.min.js"></script> <script> let qrcode = new QRCode(document.getElementById("qrcode-container"), { width: 256, height: 256 }); // 每秒刷新一次 let timer = setInterval(async () => { // 先查状态 let statusRes = await fetch('/check-bankid-status/'); let statusData = await statusRes.json(); if (statusData.status === 'complete') { clearInterval(timer); // 跳转到成功页 window.location.href = '/auth-success/'; return } if (statusData.status === 'failed' || statusData.status === 'invalid') { clearInterval(timer); alert('验证失败,请刷新重试'); return } // 状态为pending就更新二维码 let qrRes = await fetch('/get-latest-qr/'); let qrData = await qrRes.json(); if (qrData.qr_data) { qrcode.clear(); qrcode.makeCode(qrData.qr_data); } }, 1000) </script>
该实现完全避免了服务端阻塞,也不需要在本地存储二维码文件,多用户场景下不会出现冲突,符合Django开发规范。
内容的提问来源于stack exchange,提问作者Hammertime
相关产品推荐
相关产品推荐

