「 简记往来」第六篇:微信登录与JWT鉴权完整实现

📅 2026/7/2 8:23:07
「 简记往来」第六篇:微信登录与JWT鉴权完整实现
一、微信小程序登录的完整流程微信小程序登录不是“输入账号密码”而是通过微信客户端生成临时凭证由后端向微信接口兑换用户身份。核心流程1. 小程序调用 wx.login() 获取 code 2. 后端用 code AppID AppSecret 换取 openid 和 session_key 3. 后端生成 JWT token 返回给小程序 4. 小程序存储 token后续请求携带 5. 服务端验证 JWT 有效性二、前端实现2.1 wx.login 获取 code// app.jsApp({onLaunch(){wx.login({success:async(res){if(res.code){try{constresultawaitthis.login(res.code);// 登录成功}catch(err){console.error(登录失败,err);}}}});},asynclogin(code){returnnewPromise((resolve,reject){wx.request({url:https://api.jianji.com/auth/login,method:POST,data:{code},success:(res){if(res.statusCode200){const{token,userInfo}res.data.data;wx.setStorageSync(token,token);wx.setStorageSync(userInfo,userInfo);resolve({token,userInfo});}else{reject(res.data);}},fail:reject});});}});wx.login()调用是静默的不需要用户授权。用户打开小程序登录流程就已经在后台进行了。三、服务端实现3.1 用code换取openid// routes/auth.jsconstaxiosrequire(axios);constjwtrequire(jsonwebtoken);constUserrequire(../models/User);router.post(/login,async(req,res){const{code}req.body;// 1. 调用微信接口换取 openidconstwxResponseawaitaxios.get(https://api.weixin.qq.com/sns/jscode2session,{params:{appid:process.env.WECHAT_APPID,secret:process.env.WECHAT_SECRET,js_code:code,grant_type:authorization_code}});const{openid,session_key,errcode,errmsg}wxResponse.data;if(errcode){returnres.status(400).json({code:errcode,msg:errmsg});}// 2. 查找或创建用户letuserawaitUser.findOne({openid});if(!user){userawaitUser.create({openid,nickname:微信用户,avatarUrl:,lastLoginAt:newDate()});}else{user.lastLoginAtnewDate();awaituser.save();}// 3. 生成 JWT tokenconsttokenjwt.sign({userId:user._id.toString(),openid:user.openid},process.env.JWT_SECRET,{expiresIn:7d});res.json({code:0,data:{token,userInfo:user.toJSON()}});});code是临时凭证5分钟内有效且只能用一次。四、JWT鉴权中间件// middleware/auth.jsconstjwtrequire(jsonwebtoken);functionauth(req,res,next){constauthHeaderreq.headers.authorization;if(!authHeader||!authHeader.startsWith(Bearer )){returnres.status(401).json({code:40100,msg:未登录});}consttokenauthHeader.substring(7);try{constdecodedjwt.verify(token,process.env.JWT_SECRET);req.userIddecoded.userId;req.openiddecoded.openid;next();}catch(err){returnres.status(401).json({code:40100,msg:token无效或过期});}}module.exportsauth;五、前端请求携带token// services/api.jsfunctionrequest(options){consttokenwx.getStorageSync(token);returnnewPromise((resolve,reject){wx.request({...options,header:{Content-Type:application/json,Authorization:Bearer token},success:(res){if(res.statusCode401){// token过期重新登录wx.removeStorageSync(token);wx.navigateTo({url:/pages/login/login});return;}resolve(res.data);},fail:reject});});}六、Token刷新机制JWT过期后前端需要重新获取token。// app.jsletisRefreshingfalse;letrefreshSubscribers[];asyncfunctionrefreshToken(){returnnewPromise((resolve,reject){wx.login({success:async(res){try{constresultawaitapi.login(res.code);resolve(result.token);}catch(err){reject(err);}},fail:reject});});}// 在请求拦截器中处理token刷新if(res.statusCode401!options._retry){if(isRefreshing){// 等待刷新完成}else{isRefreshingtrue;try{constnewTokenawaitrefreshToken();// 重试原请求}catch(err){// 跳转登录}}}七、安全注意事项AppSecret绝对不能暴露在前端只在服务端使用JWT Secret要足够复杂建议用32位以上的随机字符串token存储用wx.setStorageSync不要用全局变量小程序重启会丢失传输必须用HTTPS防止中间人攻击八、总结微信登录的本质是OAuth 2.0的授权码模式。小程序获取code后端用code换openid再生成JWT返回前端。这套流程看起来步骤多但每个步骤都有明确的职责wx.login()获取临时凭证code2Session换取用户身份JWT保持登录状态下一篇我们来聊聊数据库选型为什么用MongoDB而不是MySQL评论区聊聊你的小程序登录方案遇到过什么问题