微信扫码登录PC后台系统的实现,及登录过期时的路由重定向方案

209 阅读1分钟

主要的流程如图:

QQ图片20210418111847.png

PC部分:

首先通过小程序官方api创建具体页面的二维码

developers.weixin.qq.com/miniprogram… (ps: scene为下文的actKey)

  /**
   * 获取小程序码
   */
  getQrcode() {
    const that = this;
    this.setState({
      codeUrl: '',
      codeStatus: 1,
    });
    return req.sys
      .getQrcode()
      .then(res => {
        if (res.code === 0) {
          const { actKey, picUrl } = res.data;
          that.actKey = actKey;
          that.setState({
            codeUrl: picUrl,
          });
          that.getQrcodeStatus();
        } else {
          req.err.show(res.msg);
        }
      })
      .catch(err => {
        req.err.show(err);
      });
  }

在pc端的login页面,获取了扫码的二维码图片和唯一的actKey后,就调个接口轮询获取小程序端是否有setWebSession,如果有就返回sessionId,并把它当为凭证设在http请求头Header中/cookie

轮询登录状态

  /**
   * 获取小程序状态
   */
  getQrcodeStatus() {
    const that = this;
    const { actKey } = this;
    clearTimeout(this.getQrcodeStatusTimer);
    return req.sys
      .getQrcodeStatus({
        actKey,
      })
      .then(res => {
        if (res.code === 0) {
          const { status } = res.data;
          that.setState({
            codeStatus: status,
          });
          switch (status) {
            case -1: // 已过期
              break;
            case 0: // 登录成功
              req.session.set(res.data.sessionId);
              setTimeout(() => {
                that.getMyCrtCompany();
              }, 1000);
              break;
            case 1: // 等待扫码
              that.getQrcodeStatusTimer = setTimeout(() => {
                that.getQrcodeStatus();
              }, 2000);
              break;
            default:
              break;
          }
        } else {
          req.err.show(res.msg);
        }
      })
      .catch(req.err.show);
  }

下面这部分的内容为路由重定向方案实现的知识补充

1、PC用的是react + ant.design

    // MDN上的location.search的使用例子
    // 声明了一个 <a id="myAnchor" href="https://developer.mozilla.org/en-US/docs/Location.search?q=123"> 元素在文档流中
    var anchor = document.getElementById("myAnchor");
    var queryString = anchor.search; // Returns:'?q=123'

2、在顶部app.js里,调用http时

if (res.code === req.err.CODE.sessionExpired) {
    req.err.goLogin();
    return;
}

3、err.js文件

const CODE = {
  sessionExpired: xxxx,
};
/**
 * 登录
 */
function goLogin() {
  const { pathname, search } = history.location;
  history.replace(`/login?url=${encodeURIComponent(pathname + search)}`);
}
/**
 * 错误信息提示
 * @param {any} err 错误对象
 */
function show(err) {
  const msg = msgPicker(err);
  if (err.code && err.code === CODE.sessionExpired) {
    return message.error('登录已过期,请重新登录', 2).then(goLogin);
  }
  return Modal.warning({
    title: '提示',
    content: msg,
  });
}

路由重定向

    /**
   * 获取当前公司 
   */
  getMyCrtCompany() {
    console.log('this.props', this.props);
    const { history, location } = this.props;
    if (location.search) {
      const urlData = searchParser(location.search) || {};
      // 形如http://193.112.220.108:3001/#/login?url=%2Fdashboard
      console.log('urlData', urlData); 
      if (urlData.url) {
        const { url } = urlData;
        if (/^http/.test(url)) {
          window.location.href = decodeURIComponent(url);
          return;
        }
        history.replace(decodeURIComponent(url));
        return;
      }
    }

    /**
     * query解析
     */
    export function searchParser(search) {
      if (!search) {
        return null;
      }
      const urlArr = search.substr(1).split('&');
      const urlData = {};
      urlArr.map(item => {
        const [key, val] = item.split('=');
        urlData[key] = val;
        return item;
      });
      return urlData;
    }

微信部分:

(ps:微信扫码只能访问线上环境)

若你需要扫体验版/开发版的环境,得在相关小程序内调用wx.scanCode

  /**
   * web登录
   */
  webLogin() {
    const appInst = getApp();
    wx.scanCode({
      success(res) {
        console.log('scanCode:', res);
        const { path } = res;
        const scene = path.split('=')[1];
        appInst.globalData.showOption.query.scene = scene;
        appInst.$opts.query.scene = scene;
        console.log('appInst.globalData:', appInst.globalData);
        // 路由跳转
        router.push({
          name: 'common.webLogin',
        });
      },
    });
  },

在最顶部的app.js里

  onShow(option) {
    console.log('App onShow option:', option);
    this.globalData.showOption = option;
    // 检查更新
  },
  globalData: {
    showOption: null,
  },

    /**
   * 获取app option
   */
  getOpt() {
    const { showOption } = this.globalData;
    // 这里的this.$opts为路由参数的封装
    const appOpts = (showOption && showOption.query && showOption.query.scene) ? showOption : this.$opts;
    if (!appOpts.query || !appOpts.query.scene) {
      return null;
    }
    return appOpts;
  },

common.webLogin 页面

const appInst = getApp();

  /**
   * 登录
   */
  login() {
    if (!appInst || !appInst.$opts) {
      return;
    }
    const { showOption } = appInst.globalData;
    const appOpts = (showOption && showOption.query && showOption.query.scene) ? showOption : appInst.$opts;
    if (!appOpts.query || !appOpts.query.scene) {
      return;
    }
    req.getSessionId()
      .then((sessionId) => {
        console.log({
          actKey: appOpts.query.scene,
          sessionId,
        });
        if (!sessionId) {
          return;
        }
        req.common.setWebSession({
          actKey: appOpts.query.scene,
          sessionId,
        })
          .then((res) => {
            if (res.code !== 0) {
              req.err.show(res.msg);
              return;
            } 
             wx.showToast({
               title: '登录成功',
             });
             setTimeout(() => {
               router.push({
                 name: 'home',
               });
             }, 1000);
          })
          .catch(req.err.show);
      });
  },

最后

这个是我们目前项目采取的方案,也希望能学习更多的相关方案,欢迎大家来讨论