node+sequelize+koa + mysql实现用户注册、登录与token签发

背景

最近在用electron写桌面应用,后来随着想法越来越多,就准备增加用户相关的功能,这就涉及到了用户的登录、注册等功能

在此简单做下记录,勉强维持下月更😂

正文

初始化项目

1-安装相关包

js 复制代码
npm i koa koa-router sequelize minimist mysql2

2-创建目录

  • app.js

项目的启动入口,用于启动koa和一些异常处理

  • koa.js

启动一个http服务

  • .env

配置相关

  • src/util

辅助方法或变量

  • src/routr文件夹

按模块管理的路由文件

  • src/db文件夹

管理数据库相关的操作,建立action和model两个子文件夹,一个专门用来定义模型,一个专用于对数据库进行增删改查

  • src/controller文件夹

逻辑层

代码开发

进入koa.js,在此处创建一个http服务

登录和注册一般需要提交post请求,所以还需要一个koa-bodyparser中间件来对参数进行解析

js 复制代码
import Koa from "koa";
import bodyParser from "koa-bodyparser";
import initController from "./src/index.js";
...

const app = new Koa();
// 解析post请求参数
app.use(bodyParser());
// 注册路由
await initController(app);
...

const server = app.listen(APP_RUN_PORT, () => {
  emitLog(`koa服务已启动:http://127.0.0.1:${APP_RUN_PORT}/...`, "success");
});
...

进入router文件夹

注册用户相关的路由,如下,注册了/user/login/user/register这两个

ts 复制代码
import Router from "koa-router";
...
export default async function (app, ...) {
  const router = new Router();

  router.prefix("/user");

  router.post("/login", async (ctx) => {
    ...
  });

  router.post('/register', async (ctx) => {
    ...
  })

  app.use(router.routes());
}

启动项目,控制台打印如下即是启动成功

现在用postman测试下刚刚注册的路由通不通,显然是通的,只不过接口里默认是什么都不处理直接返回ok的

接着到db文件夹下,通过Sequelize与mysql服务建立连接

js 复制代码
import Sequelize from "sequelize";
import initModels from "./model/index.js";
...

// 读取.env文件中定义的数据库相关的变量
const { MYSQL_OPTIONS } = process.env;
const [database, username, password, port] = MYSQL_OPTIONS.split("|");
// 建立连接
const sequelize = new Sequelize(database, username, password, {
  host: "localhost",
  port,
  dialect: "mysql",
  logging: false,
});
...
// 初始化模型
... = await initModels.call(this, sequelize);

进入model文件夹下的user.js中创建user模型,它主要包含了名称、密码和唯一的主键id (省略部分是用于创建表关联的逻辑,由于与本文内容无关,不展开)

js 复制代码
import Sequelize from "sequelize";

const TABLE_NAME = "users";

export default async function (options) {
  const { sequelize, sequelizeOptions } = options;
  const model = await sequelize.define(
    TABLE_NAME,
    {
      id: {
        type: Sequelize.STRING,
        primaryKey: true,
        autoIncrement: false,
      },
      username: {
        type: Sequelize.STRING,
        allowNull: false,
      },
      password: {
        type: Sequelize.STRING,
        allowNull: false,
      },
      ...
    },
    
    // 公共配置,用于修改或设置Sequelize的默认行为
    sequelizeOptions
  );

  ...

  return {
    model,
    ...
  };
}

进入action文件夹下的user.js中,这里是最终与数据库交互的地方,也即增删改查

js 复制代码
export default async function ({ model }) {

  ...

  async function api() {
    ...
  }

  return {
    ...,
    api,
  };
}

在不考虑行为验证、不接入验证码、不通过第三方授权的情况下,注册就只需要客户端提交用户名和密码就可以了

js 复制代码
const { username,password } = ctx.request.body || {}

接着需要对用户名进行校验,这需要用到db/action/user下定义的resolveUserListBy接口

当发现表中已经存在传入的用户名时,就直接return

js 复制代码
const repeat = await db.user.resolveUserListBy(() => ({
     username
}))
if (Array.isArray(repeat) && repeat.length) return generateResponse(ctx, 422, '用户名已存在')

只有不存在时,才向数据库表中插入

js 复制代码
const pushed = await db.user.storeUser({
   id: randomUUID(),
   username: username,
   password
})
if (pushed !== 911) return generateResponse(ctx, 200, 'ok')

再无异常的情况下,表中就有新增的用户啦

但此时的密码是明文存储的,这显然是不合理的且不安全的,所以需要对这个密码进行加密处理

这需要用到bcrypt包,并在存储时进行调用

js 复制代码
import { hashSync } from 'bcrypt';

... = await db.user.storeUser({
   id: randomUUID(),
   username: username,
   password:hashSync(password, 10)
})

再次注册个用户看一下,密码就已经是密文了

这顺带着提出了一个新问题:我咋知道当前要登录的用户的密码到底对不对呢?

找到登录接口

和注册一样先校验下用户名存在性,由于校验的逻辑几乎一致,故简单做下封装

并在login接口中调用

js 复制代码
const { username, password } = ctx.request.body || {}
const { status, message,data } = await _validateUser(username)
if (message === '用户不存在') return generateResponse(ctx, status, message)

至于密码校验,直接使用bcrypt的compareSync就可以了

js 复制代码
if (!compareSync(password, data.password)) return generateResponse(ctx, 422, '用户名或密码输入有误');

这样一来,当输入错误的密码后,就能够被正确拦截了

最后为了区分用户是否登录,我们还得为其签发身份标识,以达到某些接口不登录无法访问的效果,这就需要所谓的token了

这需要再安装个包:jsonwebtoken

然后调用它的sign方法进行签名即可

js 复制代码
generateResponse(ctx, 200, {
   username: data.username,
   // 这里可以加任意的值,比如设置过期时间
   token: jwt.sign({id: data.id}, TOKEN_SECRECT)
});

这样用户就有身份了

最后的最后,在需要鉴权的接口进行解析就好了

新建个user/information接口测试下

当token不存在或错误时及时拦截后续的处理逻辑就好啦(如果有)

相关推荐
2501_942818911 天前
AI 多模态全栈项目实战:Vue3 + Node 打造 TTS+ASR 全家桶!
vue.js·人工智能·node.js
前端流一1 天前
[疑难杂症] 浏览器集成 browser-use 踩坑记录
前端·node.js
大布布将军1 天前
⚡后端安全基石:JWT 原理与身份验证实战
前端·javascript·学习·程序人生·安全·node.js·aigc
闲云一鹤1 天前
【工具篇】使用 nvm 进行 node 版本管理
前端·npm·node.js
小白咚2 天前
npm在文件下输入运行命令,授权限制问题window
前端·npm·node.js
Dreamcatcher_AC2 天前
Node.js留言板开发全流程解析
前端·javascript·mysql·node.js·express
『六哥』2 天前
node.js 安装教程
node.js
程序猿的程2 天前
Stock写给前端的股票行情 SDK: stock-sdk,终于不用再求后端帮忙了
前端·javascript·node.js
疯狂踩坑人2 天前
【Nodejs】Http异步编程从EventEmitter到AsyncIterator和Stream
前端·javascript·node.js
WangHappy2 天前
面试官:如何优化批量图片上传?队列机制+分片处理+断点续传三连击!
前端·node.js