使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

简短回顾一下网页端的流程,总的来说网页端的职责有三:

  1. 生成一个随机字符作为鉴权会话的临时Token,
  2. 生成一个小程序码, Token作为参数固化于小程序码当中
  3. 监控整个鉴权过程状态,一旦状态变为AUTHORIZED(已授权)则获取小程序登录凭证code。调用ExternalAuthenticate完成登录。

上一章,我们介绍了服务端的开发,这次我们需要调用GetACode,GetToken,分别获取小程序码,和获取当前状态

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

vue create mp-auth

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

新建ajaxRequest.ts,创建request对象,这一对象将利用axios库发送带有访问凭证的Header的请求

这里使用js-cookie库获取cookie中的访问凭证,并添加到Header中 

import Cookies from "js-cookie"; import axios, {  CancelTokenSource } from 'axios' //发送网络请求 const tokenKey = "main_token"; const getToken = () => Cookies.get(tokenKey);  export const request = async (url: string, methods, data: any, onProgress?: (e) => void, cancelToken?: CancelTokenSource) => {     let token = null     let timeout = 3000;     if (cancelToken) {         token = cancelToken.token         timeout = 0;     }      const service = axios.create()     service.interceptors.request.use(         (config) => {             const token = getToken();             // Add X-Access-Token header to every request, you can add other custom headers here             if (token) {                 config.headers['X-XSRF-TOKEN'] = token                 config.headers['Authorization'] = 'Bearer ' + token             }             return config         },         (error) => {             Promise.reject(error)         }     )      const re = await service.request({         url: url,         method: methods,         data: data,         cancelToken: token,         timeout: timeout,         onUploadProgress: function (progressEvent) { //原生获取上传进度的事件             if (progressEvent.lengthComputable) {                 if (onProgress) {                     onProgress(progressEvent);                 }             }         },     })     return re as any; }  ///获得取消令牌 export const getCancelToken = () => {     const source = axios.CancelToken.source();     return source; } 

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

回到App.vue

我们按照网页端这个三个职责的顺序,分步骤完成代码

生成Token

首先建立两个变量,存储当前的Token和状态枚举值

export default {   name: "App",   data: () => {     return {       wechatMiniappLoginToken: null,       wechatMiniappLoginStatus: "WAIT",     };   },

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

methods中建立getToken函数,这里使用8位随机数作为token值

  methods: {     getToken() {       if (this.wechatMiniappLoginToken == null) {         var date = new Date();         var token = `${(Math.random() * 100000000)           .toFixed(0)           .toString()           .padEnd(8, "0")}`;         this.wechatMiniappLoginToken = token;       }       return this.wechatMiniappLoginToken;     }    }

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

生成小程序码

Html部分,插入一个图片,将token传入scene参数

<img :src="`${prefix}/MiniProgram/GetACode?scene=${getToken()}&page=${miniappPage}&mode=content`"/>

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

Prefix是你的服务地址前缀

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

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

page为小程序中鉴权页面的路径,需注意的是在小程序未发布时无法跳转至页面,报错41030,若要使用扫码来跳转指定页面,小程序需要先发布

miniappPage: "pages/login/index"

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

监控整个鉴权过程状态

首先需要一个函数,根据当前的Token获取当前鉴权状态,并且不断循环这一操作,这里编写start函数,并以每1秒钟轮询状态,代码如下:

   start() {       clearInterval(this.timerId);       this.timerId = setInterval(async () => {         if (!this.loading) {           this.loading = true;            await request(             `${this.prefix}/MiniProgram/GetToken?token=${this.wechatMiniappLoginToken}`,             "get",             null           )                     }       }, 1000);     }, 

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

在页面开始函数代码Created中调用这一函数

  created: function () {     this.start();   },

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

接下来处理轮询结果,如果没有拿到值,说明Token已过期,wechatMiniappLoginStatus状态为"EXPIRED"

          await request(             `${this.prefix}/MiniProgram/GetToken?token=${this.wechatMiniappLoginToken}`,             "get",             null           )             .then(async (re) => {               if (re.data.result == null) {                 this.wechatMiniappLoginStatus = "EXPIRED";                 this.wechatMiniappLoginToken = null;                 this.loading = false;               }

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

注意:

在后端项目的MiniProgramAppService.cs中,我们定义的

TokenCacheDuration为5分钟,表明二维码的有效时间为5分钟。

public static TimeSpan TokenCacheDuration = TimeSpan.FromMinutes(5);

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

相应的Token为Expired时,将wechatMiniappLoginToken置空,这一属性变动vue会通知img的src值变动而刷新小程序码,同时获取新的Token值赋值给wechatMiniappLoginToken,这也是刷新小程序码的逻辑

this.wechatMiniappLoginToken = null;

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

这样能以简单方式,实现二维码刷新功能。

界面中新建一个刷新小程序码的按钮:

      <el-button         v-if="wechatMiniappLoginToken != null"         type="primary"         size="medium"         @click="wechatMiniappLoginToken = null"         >刷新       </el-button>

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

编写一个externalLogin方法,在用于获取Code后,调用后端第三方登录接口,获取访问凭证存储于Cookie中

async externalLogin(userInfo: {       authProvider: string;       providerKey: string;       providerAccessCode: string;     }) {       let authProvider = userInfo.authProvider;       let providerKey = userInfo.providerKey;       let providerAccessCode = userInfo.providerAccessCode;        await request(         `https://localhost:44311/api/TokenAuth/ExternalAuthenticate`,         "post",         {           authProvider,           providerKey,           providerAccessCode,         }       ).then(async (res) => {         var data = res.data.result;         setToken(data.accessToken);       });     },

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

 定义setToken函数,使用js-cookie库将访问凭证写入浏览器cookie中

const tokenKey = "main_token"; const setToken = (token: string) => Cookies.set(tokenKey, token);

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

 

在此之前我们需写一个参数传递对象,为了保留一定的扩展能力,data中我们定义loginExternalForms,已经实现的微信小程序登录,则对应的authProvider值为“WeChatAuthProvider”,providerAccessCode则为生成的Token值

      loginExternalForms: {         WeChat: {           authProvider: "WeChatAuthProvider",           providerKey: "default",           providerAccessCode: "",         },       },

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

接下来包装externalLogin方法,在调用完成前后做一些操作,比如登录成功后,将调afterLoginSuccess方法

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

    async handleExternalLogin(authProvider) {       // (this.$refs.baseForm as any).validate(async (valid) => {       //   if (valid == null) {       var currentForms = this.loginExternalForms[authProvider];        this.loading = true;       return await this.ExternalLogin(currentForms).then(async (re) => {         return await request(           `${this.prefix}/User/GetCurrentUser`,           "get",           null         ).then(async (re) => {           var result = re.data.result as any;           return await this.afterLoginSuccess(result);         });       });     },      async handleWxLogin(providerAccessCode) {       this.loginExternalForms.WeChat.providerAccessCode = providerAccessCode;       return await this.handleExternalLogin("WeChat");     },  

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

afterLoginSuccess函数用于登录成功后的逻辑,停止计时器,并跳转页面,本实例仅做弹窗提示

    successMessage(value = "执行成功") {       this.$notify({         title: "成功",         message: value,         type: "success",       });     },          async afterLoginSuccess(userinfo) {       clearInterval(this.timerId);       this.successMessage("登录成功");       this.userInfo = userinfo;     },

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

继续编写start函数

如果拿到的token至不为空,则传递值给wechatMiniappLoginStatus,当wechatMiniappLoginStatus状态为"AUTHORIZED"时调用handleWxLogin函数:

              if (re.data.result == null) {                 this.wechatMiniappLoginStatus = "EXPIRED";                 this.wechatMiniappLoginToken = null;                 this.loading = false;               } else {                 var result = re.data.result;                 this.wechatMiniappLoginStatus = result.status;                 if (                   this.wechatMiniappLoginStatus == "AUTHORIZED" &&                   result.providerAccessCode != null                 ) {                   await this.handleWxLogin(result.providerAccessCode)                     .then(() => {                       this.wechatMiniappLoginToken = null;                       this.loading = false;                     })                     .catch((e) => {                       this.wechatMiniappLoginToken = null;                       this.loading = false;                       clearInterval(this.timerId);                     });                 } else {                   this.loading = false;                 }               }

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

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

界面将清晰的反映wechatMiniappLoginStatus各个状态时对应的UI交互:

WAIT(等待扫码):

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

ACCESSED(已扫码):

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

 ACCESSED(已扫码):

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

 

完整的Html代码如下:

<template>   <div id="app">     <!-- <img alt="Vue logo" src="./assets/logo.png" />     <HelloWorld msg="Welcome to Your Vue.js App" /> -->     <div style="height: 450px">       <div v-if="wechatMiniappLoginStatus == 'ACCESSED'">         <el-result           icon="info"           title="已扫码"           subTitle="请在小程序上根据提示进行操作"         >         </el-result>       </div>        <div v-else-if="wechatMiniappLoginStatus == 'AUTHORIZED'">         <el-result           icon="success"           title="已授权"           :subTitle="loading ? '请稍候..' : '正在使用微信账号登录系统'"         >         </el-result>       </div>       <div v-else class="center">         <img           :src="`${prefix}/MiniProgram/GetACode?scene=${getToken()}&page=${miniappPage}&mode=content`"         />       </div>     </div>     <div class="center">       <el-button         v-if="wechatMiniappLoginToken != null"         type="primary"         size="medium"         @click="wechatMiniappLoginToken = null"         >刷新</el-button       >     </div>     <div class="center">       <span>{{ userInfo }}</span>     </div>   </div> </template> 

使用 Abp.Zero 搭建第三方登录模块(三):网页端开发

至此我们已完成网页端的开发工作

举报
发表评论

相关文章

当前内容话题
  • 0