我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
koa教程2 微信openid登录
微信的openid,前端登录时,会发送一个code码到后端,后端接收到这个code,然后配合appid、appsecret参数,像wx发送一个get请求。
wx的请求地址:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
具体查看文档:微信官方文档-服务端
可以看到这段get请求有四个参数:
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
appid | string | 是 | 小程序 appId | |
secret | string | 是 | 小程序 appSecret | |
js_code | string | 是 | 登录时获取的 code | |
grant_type | string | 是 | 授权类型,此处只需填写 authorization_code |
js_code就时前端发送给后端的code值。
那么我们怎么快速的拼接好这个get请求地址呢?
我们可以使用node的util api,他有一个format方法,其中有一个占位符%s
format接收多个参数,第一个参数为需要改变的值,后面为改变的参数,比如:
const util = require("util");
const url = "http://www.%s.com";
console.log(util.format(url,"baidu"));
//http://www.baidu.com
大概是这么一个效果,而且这个是可以多个的,比如有两个%s
占位符,那么url后面再传入两个参数,这两个参数按顺序对应占位符。
为了方便使用,我们将wx的设置放在config文件里面
config.js:
module.exports = {
//dev、prod
env: "dev",
//数据库配置
database: {
dbName: "koa-test", //数据库名
host: "localhost", //数据库地址
port: 3306, //数据库端口
user: "root", //数据库用户名
password: "123456", //数据库密码
},
//token配置
tokenConfig: {
key: "askdhjasgdhjasghgu&^%^^%&**2164.as21s8", //加密用的key
//token过期时间,数字为s,字符则需要加上d(天),h(小时),否则默认为ms
expiresIn: "2h",
},
// 微信小程序配置
wx: {
appid: "",
appsecret: "",
loginUrl: "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
}
};
这样wx的基本配置就有了。
分层
事实上我们的业务逻辑是不建议写在api里面的,一般是将它分层出去,这里我们也将微信小程序分层出去,在项目根目录创建services文件夹,里面存放分层出去的业务代码,然后我们创建一个wx.js
文件,内容如下:
wx.js:
const util = require("util");
const axios = require("axios");
const { wx } = require("@config/config");
const { AuthFailed } = require("@core/http-error");
const { User } = require("@models/user");
const { createToken } = require("@core/token");
const { UserLevel } = require("@lib/enum");
class WXManager {
static async condeToToken(code) {
const url = util.format(wx.loginUrl, wx.appid, wx.appsecret, code);
const result = await axios.get(url);
if (result.status !== 200) {
throw new AuthFailed("openid获取失败");
}
if (result.data.errcode !== 0) {
throw new AuthFailed(`openid获取失败:${result.data.errcode} ${result.data.errmsg}`);
}
let user = await User.getUserByOpenid(result.data.openid);
//不存在就创建
if (!user) {
user = await User.registerByOpenid(result.data.openid);
}
//创建token
return await createToken(user.id, UserLevel.USER);
}
}
module.exports = {
WXManager
}
result.status不等于200说明请求失败
result.data.errcode不等于0,说明wx那边检测参数没有达到要求
这两个我们需要进行报错。
成功的话,result.data.openid是有值的。
这个时候我们就需要判断数据库里面有没有这个人的openid,毕竟他有可能已经注册过了,那么我们就直接返回这个人的user数据表信息,如果没有,我们则需要创建,然后再返回user表信息。
最终我们则需要创建一个token,将user.id传入,不建议使用openid,因为openid很危险,如果被盗取就很麻烦,每个用户针对每个小程序只有一个唯一的openid,所以,这里使用的是我们数据的自增id。
然后就是用户权限了,USER完事。
由于我们需要判断openid是否存在,以及创建用户,那么我们就需要用到user模型,于是在user模型里面,我们加上两个方法:
user.js:
const bcrypt = require("bcryptjs");
const { sequelize } = require("@core/db");
const { Sequelize, Model, DataTypes } = require("sequelize");
const { AuthFailed } = require("@core/http-error");
class User extends Model {
static async verifyEmailPassword(email, password) {
//查找对应email用户
const user = await User.findOne({
where: { email }
});
if (!user) throw new AuthFailed("账号不存在!");
//判断密码是否正确
const correct = bcrypt.compareSync(password, user.password);
if (!correct) throw new AuthFailed("密码不正确!");
//存在
return user;
}
//查找数据库是否存在openid
static async getUserByOpenid(openid) {
const user = await User.findOne({
where: {
openid
}
});
return user;
}
//创建openid用户
static async registerByOpenid(openid) {
const user = await User.create({
openid
});
return user;
}
};
User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true, //主键
autoIncrement: true, //自增
},
nickname: DataTypes.STRING,
email: {
type: DataTypes.STRING(128),
unique: true, //唯一
},
password: {
type: DataTypes.STRING,
set(val) {
const salt = bcrypt.genSaltSync(10);
const psw = bcrypt.hashSync(val, salt);
this.setDataValue('password', psw);
}
},
openid: {
type: DataTypes.STRING(64),
unique: true, //唯一
},
}, {
sequelize,
tableName: "user", //指定生成的表名
});
module.exports = {
User
}
于是乎,wx登录的业务代码我们拆分好了,现在去登录api那使用:
const { WXManager } = require("@services/wx");
//登录
router.post("/login", async (ctx) => {
const { account, password, type } =
await loginValidate(ctx.request.body);
//根据登录类型进行登录
let token;
switch (type) {
case LoginType.USER_EMAIL:
// 邮箱+密码
token = await emailLogin(account, password);
break;
case LoginType.USER_MINI_PROGRAM:
// 小程序
token = await WXManager.condeToToken(account);
break;
case LoginType.USER_MOBILE:
// 手机
break;
default:
throw new NotFound("没有相应的处理函数!")
}
//成功
throw new Success("登录成功!", { token });
// console.log(LoginType.isThisType(ctx.request.body.type))
});
这里我对Success错误对象加了个数据,就是多了一个属性data,具体就不多写了,很简单。
然后我们登录,就可以拿到token值。
评论(0)