微信扫码登录这事,前后坑比想象中多。端分c端我接手过一个老项目,离项录前端Vue,目微码登后端Spring Boot,信扫前后端彻底分离,前后部署在两台不同的端分c端服务器上。第一版扫码登录,离项录后端同事直接把二维码图片用base64塞进接口返回,目微码登前端拿到就渲染,信扫看起来挺顺。前后但用户一刷新页面,端分c端二维码就失效了,离项录得重新扫码。目微码登这体验,信扫放现在根本没法用。
后来重新设计,核心就一句话:前端轮询,后端存状态,微信回调改状态。具体流程是这样——前端页面加载时,先向后端发起一个请求,要一个唯一的登录凭证,比如UUID。后端拿到这个UUID,把它当成key,往Redis里存一个状态,初始值是"waiting",同时设置过期时间,比如两分钟。然后后端把UUID拼成微信扫码登录的链接,生成二维码返回给前端。前端拿到二维码展示给用户,同时立刻启动一个定时器,每隔两秒向后端发一次请求,问的就是"这个UUID对应的状态变了没"。
后端这时候要做的事很纯粹。微信那边扫码后,用户手机上确认,微信服务器会回调我们配置的回调地址,这个地址带着一个code参数。后端收到回调,拿这个code去微信的接口换用户信息,换成功之后,就把Redis里那个UUID对应的状态改成"success",顺便把用户信息也存进去。前端轮询到状态变成success了,就拿着这个UUID再去后端换一个真正的登录token。整个闭环就通了。
这里面最容易被忽略的细节是回调地址的配置。微信开放平台要求回调域名必须和扫码页面的域名一致,但前后端分离意味着前端域名是a.com,后端接口在b.com。这时候不能让微信直接回调b.com,而是得在a.com下放一个代理转发,或者干脆让后端提供一个中转接口,前端先请求这个中转接口拿到一个临时票据,再跳转到微信授权页,微信授权后回调到中转接口,后端再重定向回前端页面。这个坑,我见过好几个团队踩,最后都是卡在域名校验上。
还有二维码过期的问题。用户扫得慢,二维码两分钟过期,前端轮询还在跑,状态一直没变,这时候得让前端主动停止轮询,并且提示用户刷新二维码。刷新逻辑也简单,重新调一次获取UUID的接口就行,但要把旧的定时器清掉,不然一个页面同时跑好几个轮询,后端Redis里全是垃圾key。
轮询间隔也别设太短。两秒一次已经够快了,有些新手写500毫秒,用户一多,后端接口压力全在轮询上,得不偿失。微信扫码本身就有延迟,用户从拿起手机到扫完码,少说也要三五秒,两秒的轮询完全够用。
最后说个体验细节。用户扫码成功后,前端拿到token,最好马上跳转页面,别让用户再点一次按钮。很多人忽略这个,扫码完了页面没反应,用户以为没成功,又扫一次,结果后端发现UUID状态已经是success,再扫就报错。要么把状态改成"used",要么直接返回一个"已登录"的提示,别让用户陷入重复操作的死循环。
这套流程跑通之后,维护起来也省心。状态全在Redis里,后端无状态,前端只管轮询,微信那边只认回调。出问题的时候,查Redis里UUID的状态就知道卡在哪一步,比看日志猜快多了。