1、申请小程序账号

1.1、 正式账号

做小程序开发之前,首先要申请账号。,可以进入小程序注册页,根据提示注册就好。

在这里插入图片描述

1.2. 获取 AppID 和 AppSecret

注册好后,打开微信公众平台,扫码登录。进来后,点击左侧的开发管理:

在这里插入图片描述
然后打开 Node 项目的.env文件,添加上两个环境变量

WECHAT_APPID=你的小程序ID
WECHAT_SECRET=你的小程序密钥

网页底部还有服务器域名的配置,暂时没有啥用,如果将来小程序要上线,就需要设置成自己的接口地址了。
在这里插入图片描述

1.3、 测试号

再来看测试号的注册。我们现在只是为了学习开发而已,也不用那么麻烦的做注册,可以直接申请小程序测试号,

在这里插入图片描述
大家按照要求的流程,申请完成后,进入微信公众平台,扫码登录

在这里插入图片描述
里面同样的给我们了小程序ID和密钥,也需要将这两个值填写到.env中。底下的服务器域名,可以先不管。

2、 微信小程序登录

2.1、创建微信小程序

学习做微信小程序登录,就不能直接用浏览器进行开发了,必须要下载微信开发者工具。在这里插入图片描述
打开链接后,大家根据自己的操作系统,自己下载安装就好。

  • Windows,一般要选第一个。
  • 苹果电脑,一般要选最后一个。
    初次打开,会要求登录,直接用自己的微信扫码登录。

在这里插入图片描述
接着再创建项目。需要先在自己电脑里,建一个文件夹,用于保存小程序项目
在这里插入图片描述

2.2、 微信登录原理

在开始开发之前,先来学习下微信小程序登录的原理。
在这里插入图片描述

获取code -> 换取 openid -> 查询/注册用户 -> 生成token
  • 通过 wx.login() 获取到用户的code判断用户是否授权读取用户信息,调用wx.getUserInfo 读取用户数据
  • 由于小程序后台授权域名无法授权微信的域名,所以需要自身后端调用微信服务器获取用户信息
  • 通过 wx.request() 方法请求业务方服务器,后端把 appid , appsecret 和 code 一起发送到微信服务器。 appid 和 appsecret 都是微信提供的,可以在管理员后台找到
  • 微信服务器返回了 openid 及本次登录的会话密钥 session_key
  • 后端从数据库中查找 openid ,如果没有查到记录,说明该用户没有注册,如果有记录,则继续往下走
  • session_key 是对用户数据进行加密签名的密钥。为了自身应用安全,session_key 不应该在网络上传输
  • 然后生成 session并返回给小程序
  • 小程序把 session 存到 storage 里面
  • 下次请求时,先从 storage 里面读取,然后带给服务端
  • 服务端对比 session 对应的记录,然后校验有效期

2.3、 第一步:获取 code

打开小程序的app.js文件,注意看到里面登录这里。增加上一行代码

console.log(res.code)

在这里插入图片描述

2.4、 第二步:换取 openid

接着,就要用这个code,换取openid了。查看小程序登录文档:

在这里插入图片描述
里面说明,使用GET请求,发送这四个参数过去,就能获取到openid。

  • appid和secret都已经有了。
  • js_code:就是刚才获取到的code。
  • 最后一个参数,是一个固定的值,按照着文档填就好。

有个问题是,在 Node 代码里,没法直接发起请求。大家做前端开发的时候,都用过axios。现在在接口里,也可以用axios发起请求。

npm i axios

然后在routes目录,新建一个wechat.js文件,这个文件就专门放微信相关的代码。从讲义文档,粘贴下基础代码:

const express = require('express');
const router = express.Router();
const axios = require('axios');
const {sequelize, User, Order} = require('../../models');
const {success, failure} = require('../../utils/responses');
const {BadRequest, NotFound} = require('http-errors');
const jwt = require('jsonwebtoken');
const {v4: uuidv4} = require('uuid');
const userAuth = require('../../middlewares/user-auth');
const logger = require("../../utils/logger");
const moment = require('moment');

/**
 * 微信小程序登录
 * POST /wechat/sign_in
 */
router.post('/sign_in', async (req, res) => {
  try {
    const {code} = req.body;
    // 通过 code 获取微信用户 openid
    const response = await axios.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 {errcode, errmsg, openid} = response.data;

    if (errcode) {
      throw new BadRequest('微信登录失败:' + errmsg);
    }

    success(res, '登录成功。', {openid});


    if (!code) {
      throw new BadRequest('请提供微信登录 code');
    }
  } catch (error) {
    failure(res, error);
  }
});

module.exports = router;

2.5、第三步:测试接口

在这里插入图片描述

2.6、第四步:数据库用户表新增openid

现在openid已经获取到了。我们要在用户表,添加一个openid字段,用于保存当前微信用户的唯一识别信息

sequelize migration:create --name add-openid-to-user

打开生成的迁移文件,直接覆盖

'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.addColumn('Users', 'openid', {
      type: Sequelize.STRING,
    });

    await queryInterface.addIndex('Users', ['openid']);
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.removeColumn('Users', 'openid');
  }
};

运行一下迁移命令

sequelize db:migrate

然后修改模型,打开models/user.js文件,在字段定义部分,加上openid的定义:

User.init(
  {
    // 其他字段定义
    openid: DataTypes.STRING,
  }
);

2.7、第五步:查询/注册用户

回到路由,继续第二步,查询或注册用户:

// success(res, '登录成功。', { openid });

// 查找或创建用户
let user = await User.findOne({
  where: { openid: openid }
});

if (!user) {
  // 首次登录,创建新用户
  const key = uuidv4();

  // 微信用户无邮箱、无用户名、无密码信息,随机生成
  user = await User.create({
    openid: openid,
    nickname: '微信用户',
    email: `wx-${key}@clwy.cn`,
    username: `wx-${key}`,
    password: Math.random().toString(36).slice(-8),
    sex: 2,
    role: 0,
  });
}

2.7、第六步:生成 token

// 生成 JWT token
const token = jwt.sign(
  {
    userId: user.id,
  },
  process.env.SECRET,
  { expiresIn: '30d' }
);

success(res, '登录成功。', { token });

2.8、最终测试

在这里插入图片描述

2.9、其他用户接口

到此,微信登录接口就搞定了。除此外,我们还需要做几个微信上,用户相关的其他接口。

/**
 * 获取用户信息
 * GET /wechat/me
 */

router.get('/me', userAuth, async (req, res) => {
  try {
    const user = await getUser(req);
    success(res, '查询用户成功。', { user });
  } catch (error) {
    failure(res, error);
  }
})

/**
 * 更新用户信息
 * put /wechat/update_info
 */
router.put('/update_info', userAuth, async (req, res) => {
  try {
    const body = filterBody(req);
    const user = await getUser(req);
    await user.update(body);

    success(res, '更新用户信息成功。', { user });
  } catch (error) {
    failure(res, error);
  }
});

/**
 * 公共方法:查询当前用户
 * @param req
 * @returns {Promise<Model<any, TModelAttributes>>}
 */
async function getUser(req) {
  const id = req.userId;

  const user = await User.findByPk(id, {
    attributes: ['id', 'nickname', 'avatar', 'sex', 'role', 'company', 'introduce']
  });

  if (!user) {
    throw new NotFound(`ID: ${id}的用户未找到。`);
  }

  return user;
}

/**
 * 公共方法:白名单过滤
 * @param req
 * @returns {{nickname: (string|*), sex: (number|*), company: *, introduce: *, avatar: *}}
 */
function filterBody(req) {
  return {
    nickname: req.body.nickname,
    sex: req.body.sex,
    company: req.body.company,
    introduce: req.body.introduce,
    avatar: req.body.avatar,
  };
}

更多推荐