使用 Abp.Zero 搭建第三方登录模块(四):微信小程序开发

简短回顾一下微信小程序端的流程:

  1. 用户通过扫码进入小程序的鉴权页面,更新状态到ACCESSED已扫码
  2. 用户点击确认授权,微信通过wx.login()接口获取第三方登录的必要信息:Code登录凭证。

微信小程序主要为用户授权行为提供交互功能,用户在扫码之后,提供一个交互UI,如下:

使用 Abp.Zero 搭建第三方登录模块(二):服务端开发 - 林晓lx - 博客园 (cnblogs.com)中介绍了服务端已经搭建的接口,这次我们将调用Access和Authenticate,分别调用来完成已扫码和已授权状态的更新。

项目搭建

 首先使用vue-cli创建一个web项目,命名为mp-weixin

vue create -p dcloudio/uni-preset-vue mp-weixin

 在Pages下创建login/index.vue页面,作为登录授权页

目录结构如下:

pages.json:

{
    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
        {
            "path": "pages/index/index",
            "style": {
                "navigationBarTitleText": "uni-app"
            }
        },
        {
            "path": "pages/login/index",
            "style": {
                "navigationBarTitleText": "授权页"
            }
        }
    ],
    "globalStyle": {
        "navigationBarTextStyle": "black",
        "navigationBarTitleText": "uni-app",
        "navigationBarBackgroundColor": "#F8F8F8",
        "backgroundColor": "#F8F8F8"
    }
}

 login目录下新建ajaxRequire.ts, 创建request对象,这一对象将利用uni-axios-ts库发送ajax请求

import axios from 'uni-axios-ts'
//发送网络请求
export const request = async (url: string, methods, data: any, onProgress?: (e) => void, cancelToken?) => {
    let token = null
    let timeout = 3000;
    if (cancelToken) {
        token = cancelToken.token
        timeout = 0;
    }
    const service = axios.create()


service.<span class="hljs-property">interceptors.<span class="hljs-property">request.<span class="hljs-title function_">use(
    <span class="hljs-function">(<span class="hljs-params">config) =&gt; {
        config.<span class="hljs-property">header[<span class="hljs-string">'Content-Type'] = <span class="hljs-string">"application/json"
        <span class="hljs-keyword">return config
    },
    <span class="hljs-function">(<span class="hljs-params">error) =&gt; {
        <span class="hljs-title class_">Promise.<span class="hljs-title function_">reject(error)
    }
)


<span class="hljs-keyword">const re = <span class="hljs-keyword">await service.<span class="hljs-title function_">request({
    <span class="hljs-attr">headers: { <span class="hljs-string">'Content-Type': <span class="hljs-string">'multipart/form-data' },
    <span class="hljs-attr">url: url,
    <span class="hljs-attr">method: methods,
    <span class="hljs-attr">data: data,
    <span class="hljs-attr">cancelToken: token,
    <span class="hljs-attr">timeout: timeout,
    <span class="hljs-attr">onUploadProgress: <span class="hljs-keyword">function (<span class="hljs-params">progressEvent) { <span class="hljs-comment">//原生获取上传进度的事件
        <span class="hljs-keyword">if (progressEvent.<span class="hljs-property">lengthComputable) {
            <span class="hljs-keyword">if (onProgress) {
                <span class="hljs-title function_">onProgress(progressEvent);
            }
        }
    },
})
<span class="hljs-keyword">return re <span class="hljs-keyword">as any;

}

///获得取消令牌
export const getCancelToken = () => {
const source = axios.CancelToken.source();
return source;
}

index.vue中创建loginExternalForms作为参数传输对象

export default {
  data() {
    return {
      loginExternalForms: {
        WeChat: {
          token: "",
          providerAccessCode: "",
        },
      }
    };
  }
}

onLoad函数中,option存有扫描小程序码中的scene参数,将scene参数赋值给token变量

 onLoad(option) {
    this.loginExternalForms.WeChat.token = option.scene;
    this.start();
  },

start中我们调用Access接口,更改状态至ACCESSED(已扫码) ,若返回成功,则提示点用户点击确认授权,若返回的结果异常"WechatMiniProgramLoginInvalidToken"时,表明此时小程序码已过期,需在网页端更新小程序码。

    async start() {
      var currentForms = this.loginExternalForms["WeChat"];
      this.loading = true;
      await request(`${this.prefix}/MiniProgram/Access`, "post", currentForms)
        .then((re) => {
          this.successMessage("您已扫描二维码,请点击确认登录以完成");
        })
        .catch((c) => {
          var err = c.response?.data?.error?.message;
          if (err != null) {
            if (err == "WechatMiniProgramLoginInvalidToken") {
              this.isInvalid = true;
            } else {
              this.errorMessage(c.err);
            }
          }
        })
        .finally(() => {
          setTimeout(() => {
            this.loading = false;
          }, 1.5 * 1000);
        });
    },

Prefix是你的服务地址前缀

prefix: "https://localhost:44311/api/services/app"

在Html中,我们创建授权登录与取消按钮,仅当isInvalid 为true时可以点击授权

      <button
        @click="handleWxLogin"
        :disabled="isInvalid || loading"
        class="sub-btn"
      >
        授权登录
      </button>

  <span class="hljs-tag">&lt;<span class="hljs-name">button @<span class="hljs-attr">click=<span class="hljs-string">"cancelWxLogin" <span class="hljs-attr">:disabled=<span class="hljs-string">"loading" <span class="hljs-attr">class=<span class="hljs-string">"sub-btn"&gt;
    取消
  <span class="hljs-tag">&lt;/<span class="hljs-name">button&gt;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>

创建 handleExternalLogin用于处理用户点击授权登录后的逻辑,调用Authenticate接口,更新状态至AUTHORIZED(已授权)在此之前需要调用uni.login获取小程序登录凭证code。

有关uni.login函数,请参考官方文档uni.login(OBJECT) | uni-app官网 (dcloud.io)

uniapp支持多种小程序,为了保留一定的扩展能力,handleExternalLogin函数中我们保留参数authProvider,已实现的微信小程序登录handleWxLogin函数调用时传递参数"WeChat",

    async handleExternalLogin(authProvider) {
      var currentForms = this.loginExternalForms[authProvider];
      this.loading = true;
      await request(
        `${this.prefix}/MiniProgram/Authenticate`,
        "post",
        currentForms
      )
        .then((re) => {
          uni.redirectTo({
            url: "/pages/index/index",
          });
        })
        .catch((c) => {
          var err = c.response?.data?.error?.message;
          if (err != null) {
            if (err == "WechatMiniProgramLoginInvalidToken") {
              this.isInvalid = true;
            } else {
              this.errorMessage(c.err);
            }
          }
          setTimeout(() => {
            this.loading = false;
          }, 1.5 * 1000);
        });
    },


async handleWxLogin() {
const that = this;
uni.login({
provider: “weixin”,
success: (loginRes) => {
that.loginExternalForms.WeChat.providerAccessCode = loginRes.code;
that.handleExternalLogin(“WeChat”);
},
});
},

创建取消登录函数

    cancelWxLogin() {
      uni.redirectTo({
        url: "/pages/index/index",
      });
    },

执行成功通知函数

    successMessage(value = "执行成功") {
      uni.showToast({
        title: value,
        icon: "success",
        duration: 1.5 * 1000,
      });
    },

接下来简单编写一个界面,

界面将清晰的反映isInvalid与loading状态时对应的UI交互:

正常

小程序码过期 

整体测试

模拟器测试

打开网页后,将图像另存为

 

 在微信小程序调试工具,“通过二维码编译”

 等待手机界面显示授权页面后点击“授权登录”:

 

 GetCurrentUser接口返回正确数据,并显示于web页面之上

 

 

至此完成了小程序端的开发工作

项目地址

jevonsflash/abp-mp-auth (github.com)

 

结束语

小程序登录具有一定的扩展性,虽然通篇介绍微信小程序登录,但登录鉴权作为小程序抽象功能,uniapp集成了各个平台(微信、支付宝、百度、字节跳动小程序)的登录接口,通过uni.login可以获取相应平台的code

使用 Abp.Zero 搭建第三方登录模块(四):微信小程序开发

https://blog.matoapp.net/posts/74407f40/

作者

林晓lx

发布于

2022-07-19

更新于

2024-09-11

许可协议

评论