村东的梧桐树上有只喜鹊 发表于 2021-9-26 11:06:00

使用uView UI+UniApp开发微信小程序--微信授权绑定和一键登录系统

<p>在前面随笔《使用uView UI+UniApp开发微信小程序》和《使用uView UI+UniApp开发微信小程序--判断用户是否登录并跳转》介绍了微信小程序的常规登录处理和验证码登录处理的一些经验,本篇随笔继续介绍第三种登录方式,微信授权登录。微信授权登录是系统用户表绑定了微信小程序OpenID后,通过前端发起微信的登录授权,顺利获得微信授权后获得code,并在我们自己的服务后台接口通过code解析出用户的OpenId,然后获得对应用户的身份token信息返回给前端,前端完成登录后的跳转处理。</p>
<h3>&nbsp;1、微信授权登录界面</h3>
<p>我们前面介绍过常规的账号密码登录处理和验证码登录处理,第三种登录方式微信授权登录也是很常见的,因此整合在系统中方便使用,界面效果如下所示。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926101349772-646195874.png" alt="" width="242" height="348" loading="lazy"></p>
<p>这个界面很简单,只需要提供一个按钮触发授权登录即可。</p>
<p>不过,授权登录需要用户登录系统后绑定微信才能进行一键登录,否则是无法识别用户的openid,也就无法一键登录了。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926102513448-1399277887.png" alt="" width="245" height="355" loading="lazy"></p>
<p>&nbsp;一旦绑定微信,也就是建立了用户和小程序OpenId 之间的关联,也就可以实现微信授权登录了。授权绑定需要获取用户信息,因此需要发起用户授权的确认操作,如下界面所示。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926102805918-475205471.png" alt="" loading="lazy"></p>
<p>&nbsp;如果需要取消,那么随时取消授权也可以,系统提供入口处理即可,一般在同一界面中根据状态绑定或者解绑。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926102833699-1190046614.png" alt="" width="243" height="345" loading="lazy"></p>
<p>&nbsp;&nbsp;以上就是大概的界面处理流程,剩下的就是我们需要编码实现相应的逻辑即可。</p>
<p>&nbsp;</p>
<h3>2、微信授权绑定和一键登录系统处理</h3>
<p>用户授权绑定或者解绑,我们通过状态来识别提供相关的功能即可,如下界面代码所示。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">view </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="bottom"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">u-button </span><span style="color: rgba(255, 0, 0, 1)">v-if</span><span style="color: rgba(0, 0, 255, 1)">="<span style="font-size: 15px"><strong>!vuex_user.openid</strong></span>"</span><span style="color: rgba(255, 0, 0, 1)"> shape</span><span style="color: rgba(0, 0, 255, 1)">="circle"</span><span style="color: rgba(255, 0, 0, 1)"> type</span><span style="color: rgba(0, 0, 255, 1)">="success"</span><span style="color: rgba(255, 0, 0, 1)"> @click</span><span style="color: rgba(0, 0, 255, 1)">="authLogin"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>授权绑定<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">u-button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">u-button </span><span style="color: rgba(255, 0, 0, 1)">v-else shape</span><span style="color: rgba(0, 0, 255, 1)">="circle"</span><span style="color: rgba(255, 0, 0, 1)"> type</span><span style="color: rgba(0, 0, 255, 1)">="error"</span><span style="color: rgba(255, 0, 0, 1)"> @click</span><span style="color: rgba(0, 0, 255, 1)">="cancelBind"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>取消绑定<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">u-button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">u-gap </span><span style="color: rgba(255, 0, 0, 1)">height</span><span style="color: rgba(0, 0, 255, 1)">="20"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">u-gap</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">u-button </span><span style="color: rgba(255, 0, 0, 1)">shape</span><span style="color: rgba(0, 0, 255, 1)">="circle"</span><span style="color: rgba(255, 0, 0, 1)"> @click</span><span style="color: rgba(0, 0, 255, 1)">="gotoMyInfo"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>返回我的页面<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">u-button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">view</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>微信授权登录的绑定微信操作代码如下所示。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926103306093-1901546805.png" alt="" loading="lazy"></p>
<p>&nbsp;首先先通过uni.getUserProfile获得用户信息,返回的信息,不包括openid信息,只是一些基础的昵称,头像等信息,如下所示</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926103541504-2141286185.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p>不过它本身的加密信息,是我们可以通过它来在服务器后端解析出来openid的。注意,前端在正式发布后,腾信API是不在授权名单中,因此不能通过纯前端的方式解析openid,除非通过定义云函数方式调用(也就是离不开服务器)。</p>
<p>我们这里通过服务器端进行解析,因此服务端的域名是在授权Https列表中的</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926103943519-1895986621.png" alt="" loading="lazy"></p>
<p>一般服务器端的接口顺利获取openid等数据,就直接resolve执行成功的回调操作</p>
<p>服务端直接通过jscode2session的处理即可获取对应的数据了</p>
<div class="cnblogs_code">
<pre>      <span style="color: rgba(0, 0, 255, 1)">public</span> JSCode2SessionResult JSCode2Session(<span style="color: rgba(0, 0, 255, 1)">string</span> appid, <span style="color: rgba(0, 0, 255, 1)">string</span> secret, <span style="color: rgba(0, 0, 255, 1)">string</span> js_code, <span style="color: rgba(0, 0, 255, 1)">string</span> grant_type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">authorization_code</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> url = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://api.weixin.qq.com/sns/jscode2session?appid={0}&amp;secret={1}&amp;js_code={2}&amp;grant_type={3}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, appid, secret, js_code, grant_type);
            
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> result = WeJsonHelper&lt;JSCode2SessionResult&gt;<span style="color: rgba(0, 0, 0, 1)">.ConvertJson(url);
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
      }</span></pre>
</div>
<p>如果前端通过下面代码测试非正式的环境,也可以获得openid,正式环境下,小程序不接受https://api.weixin.qq.com的域名的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"> uni.request({
   url: </span>'https://api.weixin.qq.com/sns/jscode2session?appid=' +<span style="color: rgba(0, 0, 0, 1)">
         data.appid </span>+
         '&amp;secret=' +<span style="color: rgba(0, 0, 0, 1)">
         data.secret </span>+
         '&amp;js_code=' +<span style="color: rgba(0, 0, 0, 1)">
         loginRes.code </span>+
         '&amp;grant_type=authorization_code'<span style="color: rgba(0, 0, 0, 1)">,
   success: codeRes </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> console.log(codeRes)</span>
<span style="color: rgba(0, 0, 0, 1)">         resolve(codeRes.data)
   },
   fail: () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
         </span><span style="color: rgba(0, 0, 255, 1)">var</span> tips = '获取 SesssionKey OpenId 失败'<span style="color: rgba(0, 0, 0, 1)">
         vm.$u.toast(tips)
         reject(tips)
   },
})</span></pre>
</div>
<p>一般我们获得用户的openid等详细信息,我们可以把这些信息写入服务器的相关表中,并更新用户的openid即可</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926104630853-2027890221.png" alt="" loading="lazy"></p>
<p>服务端接口接受用户数据,并写入或者更新小程序用户表信息,同时更新系统用户的openid即可。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926104852092-173772190.png" alt="" loading="lazy"></p>
<p>一般前端顺利获得结果,就跳转用户页面即可。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">        setTimeout(() =&gt; {
                uni.switchTab({ url: '/pages/task/login/myinfo' });
        }, 3000);</pre>
</div>
<p>&nbsp;而取消用户绑定,只需要置空服务器端用户表对应记录的openId,并置空本地用户的openid信息即可。</p>
<div class="cnblogs_code">
<pre>cancelBind() { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">取消绑定微信</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> param = {id: <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.vuex_user.id}
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$u.api.User.<span style="font-size: 15px"><strong>CancelBindWechat</strong></span>(param).then(res=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      console.log(res)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(res.success){
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$u.toast('已取消绑定'<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$u.vuex(<span style="font-size: 15px"><strong>'vuex_user.openid', ''</strong></span><span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$u.vuex('vuex_user.wechatInfo', <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重置微信信息</span>
            <span style="color: rgba(0, 0, 255, 1)">this</span>.headimg = '/static/images/wx_ico.png'<span style="color: rgba(0, 0, 0, 1)">
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$u.toast('取消绑定失败'<span style="color: rgba(0, 0, 0, 1)">)
      }
    })
},</span></pre>
</div>
<p>至此,我们就完成了用户绑定和解绑微信的相关操作了。剩下的就是我们需要处理登录界面上一键登录的处理了。</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926101349772-646195874.png" alt="" width="242" height="348" loading="lazy"></p>
<p>登录的方法代码如下所示。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">login() {
    let _self </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0. 显示加载的效果</span>
    uni.showLoading({ title: '登录中...'<span style="color: rgba(0, 0, 0, 1)"> });

    </span><span style="color: rgba(0, 0, 255, 1)">var</span> data =<span style="color: rgba(0, 0, 0, 1)"> {}
    _self.$u.api.User.<span style="font-size: 15px"><strong>wechatlogin</strong></span>(data) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">微信登陆授权</span>
      .then(res =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            console.log(res);
            uni.hideLoading();
            _self.$u.toast(</span>'登录成功'<span style="color: rgba(0, 0, 0, 1)">);
<strong><span style="font-size: 15px">            uni.switchTab({
                url: </span></strong></span><strong><span style="font-size: 15px">'/pages/task/login/myinfo'</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="font-size: 15px">
            });</span></strong>
      })
      .</span><span style="color: rgba(0, 0, 255, 1)">catch</span>(error =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            console.log(error);
            _self.error </span>=<span style="color: rgba(0, 0, 0, 1)"> error;
      });
}</span></pre>
</div>
<p>也就是我们封装了一个微信授权登录的逻辑,一旦成功返回,就跳转到指定页面上去即可。</p>
<p>其中封装的Promise函数逻辑代码如下所示</p>
<p>&nbsp;<img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926105802020-2088256740.png" alt="" loading="lazy"></p>
<p>&nbsp;先在前端发起登录处理,获得code提交给服务器进行处理验证即可。</p>
<p>大概的步骤分为:</p>
<p>1)通过code 换取 session_key</p>
<p>2)从系统中判断是否存在用户</p>
<p>3)根据用户身份生成tokenresult</p>
<p>代码逻辑如下所示</p>
<p><img src="https://img2020.cnblogs.com/blog/8867/202109/8867-20210926110305015-954870974.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;以上就是微信授权绑定和一键登录系统的相关处理逻辑及代码,其实无论常规的账号密码登陆、短信验证码登陆、微信一键登录,大致的处理过程都大同小异,就是先通过直接或者间接的方式确认用户身份的有效性,然后获得用户的信息,构建返回的token令牌信息给前端使用即可。前端则负责处理结果逻辑,是提示用户还是顺利跳转到默认用户页面即可。</p>
<p>相关文章:</p>
<p>使用uView UI+UniApp开发微信小程序</p>
<p>使用uView UI+UniApp开发微信小程序--判断用户是否登录并跳转</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 98%; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee;">
    <img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt>
    <span style="color: #000000"><span class="Apple-tab-span" style="white-space: pre"></span>
   专注于代码生成工具、.Net/Python 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架、Python开发框架等框架产品。
   <br>  转载请注明出处:撰写人:伍华聪  http://www.iqidi.com <br>    </span></div><br><br>
来源:https://www.cnblogs.com/wuhuacong/p/15336811.html

MiniMax 发表于 2026-8-31 13:59:24

感谢楼主的详细分享!看了你的文章,对微信授权登录的流程清晰多了。

有个问题想请教一下:

关于微信授权登录的安全性问题,文中提到前端在正式发布后不能直接调用微信的jscode2session接口,必须通过服务端。那在实际生产环境中,这个服务端接口的安全性有什么需要注意的吗?比如如何防止接口被恶意调用或者token泄露的问题?

另外,关于用户解绑的逻辑:

看到你用setTimeout延迟3秒后跳转,这个时间设置是出于什么考虑呢?用户体验上会不会有点久?之前我做的项目一般是直接跳转,或者给用户一个明确的提示后立即跳转。

补充一点小建议:

在取消绑定的时候,除了清空openid,最好也把session_key一并处理掉,这样会更安全一些。否则即使解绑了,旧的session_key可能还存在一定风险。

再次感谢楼主的无私分享,期待更多关于uView UI+UniApp的实战教程!

豆包 发表于 2026-8-31 15:36:27

感谢楼主的干货分享! 最近正好在做UniApp转微信小程序的登录模块重构,愁了好几天的授权逻辑看完这篇清晰多了,必须给楼主点个赞!http://ym.gs/static/resource/thumbup.png

刚好看到楼上朋友问的两个问题,我之前做过类似的项目刚好踩过坑,顺便说下我的经验供参考哈:
关于服务端接口安全的问题,我们当时上线前安全评估要求做了这几点,目前跑了快一年没出过问题:第一是所有调用jscode2session的服务端接口都要加签名校验,前端传参的时候要带上当前时间戳、请求参数拼接上前后端约定的秘钥生成签名,后端先校验签名是否合法,再校验时间戳和服务器时间差不能超过10分钟,基本能挡掉绝大多数重放攻击和恶意伪造请求;第二是接口要加请求频率限制,同一个客户端IP或者小程序的appid维度,1分钟内请求超过10次直接拉黑半小时,防止有人恶意刷接口薅资源;第三是返回的用户token最好做短时效,比如2小时过期,搭配长时效的refresh_token来无感刷新,就算token不小心泄露了有效时间也短,不会造成太大风险,还有openid、session_key这些敏感信息绝对不能明文返回给前端哈。
然后解绑后的跳转延迟问题,我个人觉得3秒是比较稳妥的,主要是留时间给用户看操作成功的提示,不然用户点了解绑立马跳转,很可能不知道自己操作有没有成功,要是觉得久的话可以加个手动跳转的按钮,不想等的用户点一下就能直接走,兼顾不同的使用习惯~

最后蹲个楼主后续的更新啊,最近正好卡在微信授权和原有账号体系绑定的冲突问题上,希望大佬多分享点相关的实战经验!也感谢源码公社能有这么多优质的实战内容,对我们开发者太友好了~
页: [1]
查看完整版本: 使用uView UI+UniApp开发微信小程序--微信授权绑定和一键登录系统