木灵鱼儿
阅读:1006
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值。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
centos 安装最新版 node.js
[hide]先用yum 安装一次node先升级yum update 提示下载程序,回复y安装nodeyum install nodejsyum安装的node一般都不是最新的,所以我们还需要升级node的版本npm i n -g全局安装一个n插件n lts表示安装最新的稳定版!一些常用命令n ls查看已安装的node版本n rm 16.0.3删除16.0.3版本更多功能阅读插件文档:n等待安装完毕后,此时其实版本已经安装好了,但是输入node -v还是低版本,是因为centos的用户缓存问题。我们退出登录再登录就会刷新缓存了,此时的node -v输出的就是最新版本exit 退...
node npm yarn 如何同时运行多个指令
node的bash命令其实是有对应的指令字符的,但是,这个指令只能再linux,mac上才有生效,再windows是无效的,所以了解一下就行。命令说明&&顺序执行多条命令,当碰到执行出错的命令后将不执行后面的命令&并行执行多条命令||顺序执行多条命令,当碰到执行正确的命令后将不执行后面的命令|管道符windows上进行多命令"scripts": { "dev": "cross-env NODE_ENV=development webpack --env development --progress -...
koa教程2 HttpBearerAuth 传递token
前端发送token到后端,一般有三种方式:链接中携带token,query参数post这种请求body中携带token头信息携带token但是事实上http已经为我们提供了传token的方式,是一个相对来说比较安全的方式,就是HttpBearerAuth 其实他也是一个头信息来着,只是相对来说多了一点操作,在apipost软件中,我们可以直接可视化设置如果是axios,则是需要添加一个头信息:axios({ method: 'get', url: url, responseType: 'blob', headers: { 'Authoriz...

koa教程2 权限分级控制
在之前的代码中,我们使用了一个scope属性来表示用户的权限,但是scope是一个数字,他的阅读性是非常差的,当其他人开发的时候,可能并不知道你这个数字是什么意思?所以,我们需要将它转义成文本。为了方便判断,我们在enum.js中增加一个用户等级的对象enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Object.keys(this).find(key => { if (this[key] === val) { return flag = true; } })...
koa教程2 token鉴权
之前我们在token中传入我们的自定义参数,现在我们要做一个token鉴权,很简单,如果他的token合法,就可以访问这个路由,如果不合法,就打回。这个也不能说是真正意义上的鉴权,因为他还没有判断scope值,所以只能说是简单判断。做这个判断我们就可以通过中间件的形式,于是我们在middlewares目录下创建一个auth.js文件auth.js:const parseBearerToken = require("@utils/paresBearerToken"); const jwt = require("jsonwebtoken"); cons...
koa教程2 用户登录
登录类型用户注册之后,自然是用户登录,登录的时候我们需要考虑到登录的方式,往往在正式的项目里面,登录的方式可能会有很多种:账号密码登录手机号登录小程序一键登录那么我们登录的时候就要进行判断,设置一个type的属性,用于在登录时判断用户是什么类型的登录,这个由前端那边设定参数。type:1为账号密码登录,2为手机号登录,3为小程序一键登录作为判断,我们可以写一个查询表(枚举),在项目根目录创建一个lib目录,在里面创建一个enum.js文件。enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Obje...
node 自定义路径别名
用过vue cli的话,对路径中的@应该不陌生,他表示根目录,但是在node的环境里面,我们编写模块并引入时,并没有这种方便的写法。在node环境中,我们可以通过process.cwd()来表示根目录,但是这样的话,每次使用绝对路径引入,路径总是需要写一大堆拼接。const db = require(`${process.cwd()}/config/config`).database;这样略显麻烦,于是我们可以在全局写一个引入插件的方法:global.myRequire = function (path) { return require(`${process.cwd()}/${pa...
koa教程2 密码加密
默认情况下,我们通过User.create({ email, password, nickname });模型传入的数据,是无加密的,也就是说,在数据库中密码是明文显示的,这样肯定是不行,我们需要使用bcrypt插件进行加密yarn add bcryptjs --dev打开models/user.jsuser.js:const bcrypt = require("bcryptjs"); const { sequelize } = require(`${process.cwd()}/core/db`); const { Sequelize, Model, DataTy...

koa教程2 使用sequelize连接数据库,创建用户模型
数据库的连接处理,我们需要使用sequelize插件。这个插件还有对应的数据库驱动,如果是使用mysql,那么驱动是mysql2,具体可以查看中文版的入门教程:入门yarn add sequelize mysql2 --dev安装完成后我们先需要创建数据库连接。连接数据库在根目录/core目录**下创建一个db.js文件用于连接数据库。在连接数据库之前,我们需要一些数据库的参数,如账号,密码,端口,数据库名字,数据库地址,为了方便管理配置,我们在根目录创建一个config目录,里面存放一config.js文件 ,这个之前的教程我们就已经创建好了。config.js:module.expo...
koa教程2 用户注册及Joi校验、成功信息返回
用户注册用户数据表搭建完毕后,我们肯定是需要注册用户的,那么首先我们需要创建一个路由api了。在根目录:/app/router/v1目录下创建一个user.js文件。user.js:const Router = require("koa-router"); const router = new Router({ prefix: "/v1/user" }); const { User } = require(`${process.cwd()}/models/user`); //注册用户 router.post("/register&q...
