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不存在或错误时及时拦截后续的处理逻辑就好啦(如果有)

相关推荐
西门吹-禅10 小时前
prisma
node.js
怪兽毕设11 小时前
基于SpringBoot的选课调查系统
java·vue.js·spring boot·后端·node.js·选课调查系统
心.c14 小时前
Vue3+Node.js实现文件上传分片上传和断点续传【详细教程】
前端·javascript·vue.js·算法·node.js·哈希算法
roamingcode14 小时前
我是如何 Vibe Coding,将 AI CLI 工具从 Node.js 迁移到 Rust 并成功发布的
人工智能·rust·node.js·github·claude·github copilot
Stream_Silver2 天前
【Node.js 安装报错解决方案:解决“A later version of Node.js is already installed”问题】
node.js
Anthony_2312 天前
基于 Vue3 + Node.js 的实时可视化监控系统实现
node.js
说给风听.3 天前
解决 Node.js 版本冲突:Windows 系统 nvm 安装与使用全指南
windows·node.js
森叶3 天前
Node.js 跨进程通信(IPC)深度进阶:从“杀人”的 kill 到真正的信号
node.js·编辑器·vim
虹科网络安全3 天前
艾体宝新闻 | NPM 生态系统陷入困境:自我传播恶意软件在大规模供应链攻击中感染了 187 个软件包
前端·npm·node.js
摇滚侠3 天前
PNPM 包管理工具和 NPM 包管理工具
vscode·npm·node.js·pnpm