从零到一完成Midway.js登录、注册、鉴权功能

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

前言

本文将从项目搭建到实现从零到一开发一个登录、注册、鉴权的简易版注册登录系统,主要功能和技术选型如下:

  • 服务端框架———Midway.js;
  • 密码加密存储———bcrypt.js;
  • 数据库存储———typeormmysql;
  • 登录鉴权———jwt;

准备工作

安装mysql环境、建好数据库和一张user表,Dbeavervscode database用于初始化表字段。

image.png

全流程

首先我们创建一个Midway项目。

npm init midway@latest -y

初始化数据库环境

然后第一步先初始化项目数据库环境,连接mysql,安装数据库相关依赖包。

npm i @midwayjs/typeorm@3 typeorm mysql2 --save

然后在configuration.ts中引入typeorm组件:

// configuration.ts
import { Configuration } from '@midwayjs/core';
import * as orm from '@midwayjs/typeorm';
import { join } from 'path';@Configuration({imports: [// ...orm                                                         // 加载 typeorm 组件],importConfigs: [join(__dirname, './config')]
})
export class MainConfiguration {}

然后在config目录中配置数据库信息:

import { MidwayConfig } from '@midwayjs/core';
import { User } from '../entity/user.entity';export default {// use for cookie sign key, should change to your own and keep securitykeys: '1697424147281_6188',koa: {port: 7001,},typeorm: {dataSource: {default: {/*** 单数据库实例*/type: 'mysql',host: 'localhost',port: 3306,username: 'root',password: 'xxxxx',database: '数据库名',synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true,注意会丢数据logging: false,// 配置实体模型entities: [User],},},},
} as MidwayConfig;

最后我们还需要一个数据表实例,新建/entity/user.entity.ts,代码如下:

import { Entity, Column, PrimaryColumn } from 'typeorm';@Entity('userInfo')
export class User {@PrimaryColumn()id: number;@Column()username: string;@Column()password: string;
}

至此关于数据库配置环境已经OK了,项目已经和数据库关联起来了。

登录注册接口

然后新建一个user.controller.tsuser.service.ts,controller用于中转服务,service用于存放业务逻辑代码。

user.controller.ts:

import { Inject, Controller, Post } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
import { UserService } from '../service/user.service';@Controller('/api')
export class APIController {@Inject()ctx: Context;@Inject()userService: UserService;@Post('/register')async register() {const params = this.ctx.request.body as {username: string;password: string;};const user = await this.userService.register(params);return { success: true, message: 'OK', data: user };}@Post('/login')async login() {const params = this.ctx.request.body as {username: string;password: string;};const user = await this.userService.login(params);return { success: true, message: 'OK', data: user };}
}

我们再把service的雏形给写出来,代码如下:

user.service.ts

import { Provide, httpError, Inject, Context } from '@midwayjs/core';
import { User } from '../entity/user.entity';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
const { v4: uuidv4 } = require('uuid');
import { JwtService } from '@midwayjs/jwt';@Provide()
export class UserService {@InjectEntityModel(User)userModal: Repository<User>;@Inject()jwtService: JwtService;@Inject()ctx: Context;async register(options: { username: string; password: string }) {const { username, password } = options;return {success: true,username,res: '注册成功',};}async login(options: { username: string; password: string }) {const { username, password } = options;return {accessToken: 'xxxx',};}
}

前面已经让项目和数据库关联了,现在需要让接口与数据表绑定起来,我们可以通过InjectEntityModel在接口服务中注入表信息,来进行增删改查操作,有了操作数据库的能力,就可以开始开发主体逻辑了。

注册

user.service.ts:

/** @Author: 幻澄* @Date: 2023-10-16 10:42:27* @LastEditors: 幻澄* @LastEditTime: 2023-10-16 16:06:07* @FilePath: /midway-jwt/src/service/user.service.ts*/
import { Provide, httpError, Inject, Context } from '@midwayjs/core';
import { User } from '../entity/user.entity';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
const { v4: uuidv4 } = require('uuid');
import { JwtService } from '@midwayjs/jwt';@Provide()
export class UserService {@InjectEntityModel(User)userModal: Repository<User>;@Inject()jwtService: JwtService;@Inject()ctx: Context;async register(options: { username: string; password: string }) {const { username, password } = options;const user = new User();const findRes = await this.userModal.findOne({where: {username,},});if (findRes) return new httpError.BadRequestError('用户已存在');user.id = uuidv4();user.username = username;user.password = password;const res = await this.userModal.save(user);return {success: true,username,res: '注册成功',};}async login(options: { username: string; password: string }) {const { username, password } = options;return {accessToken: 'xxxx',};}
}

注册接口的逻辑如下:

  1. 获取请求参数usernamepassword
  2. user表查重,重复则响应异常;
  3. 生成ID,落库,响应用户信息;

我们通过ApiScout来模拟请求:

image.png

可以看到,注册信息被插入到数据表中了:

image.png

相同的入参再调一次,会返回重复用户的异常信息:

image.png

这样功能实现了,但是有个安全问题————账号密码应该加密存储在数据表中,因此我们使用bcryptjs来解决。

npm i bcryptjs --save

安装好之后我们将password加密一下,改造后的代码如下:

import { Provide, httpError, Inject, Context } from '@midwayjs/core';
import { User } from '../entity/user.entity';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
const { v4: uuidv4 } = require('uuid');
import { JwtService } from '@midwayjs/jwt';
import * as bcryptjs from 'bcryptjs';@Provide()
export class UserService {@InjectEntityModel(User)userModal: Repository<User>;@Inject()jwtService: JwtService;@Inject()ctx: Context;async register(options: { username: string; password: string }) {const { username, password } = options;const user = new User();const findRes = await this.userModal.findOne({where: {username,},});if (findRes) return new httpError.BadRequestError('用户已存在');user.id = uuidv4();user.username = username;user.password = bcryptjs.hashSync(password, 10);const res = await this.userModal.save(user);return {success: true,username,res: '注册成功',};}async login(options: { username: string; password: string }) {const { username, password } = options;return {accessToken: 'xxxx',};}
}

OK,至此,注册接口就开发好了。

登录

有了注册,登录就大差不差了,简易版代码如下:

import { Provide, httpError, Inject, Context } from '@midwayjs/core';
import { User } from '../entity/user.entity';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
const { v4: uuidv4 } = require('uuid');
import { JwtService } from '@midwayjs/jwt';
import * as bcryptjs from 'bcryptjs';@Provide()
export class UserService {@InjectEntityModel(User)userModal: Repository<User>;@Inject()jwtService: JwtService;@Inject()ctx: Context;async register(options: { username: string; password: string }) {const { username, password } = options;const user = new User();const findRes = await this.userModal.findOne({where: {username,},});if (findRes) return new httpError.BadRequestError('用户已存在');user.id = uuidv4();user.username = username;user.password = bcryptjs.hashSync(password, 10);const res = await this.userModal.save(user);console.log(55, res);return {success: true,username,res: '注册成功',};}async login(options: { username: string; password: string }) {const { username, password } = options;const findRes = await this.userModal.findOne({where: {username,},});if (!findRes) return new httpError.BadRequestError('不存在该用户');const compareRes: boolean = bcryptjs.compareSync(password,findRes.password);if (!compareRes) return new httpError.BadRequestError('密码错误');return {success: true};}
}

登录接口主要做了这些事情:

  1. 获取请求带来的usernamepassword
  2. user表查用户名,不存在的话返回异常信息;
  3. 通过bcryptjs将登陆的明文密码和注册落库的加密密码比较,如果密码错误,返回异常信息;
  4. 登录完成;

JWT

接下来我们加入鉴权,完善整个登录系统业务流程。

首先安装依赖包:

npm i @midwayjs/jwt --save

然后在configuration.ts中引入JWT组件:

import { Configuration, IMidwayContainer } from '@midwayjs/core';
import { IMidwayContainer } from '@midwayjs/core';
import * as jwt from '@midwayjs/jwt';@Configuration({imports: [// ...jwt,],
})
export class MainConfiguration {// ...
}

然后在config中加入JWT加密配置信息:

// src/config/config.default.ts
export default {// ...jwt: {secret: 'xxxxxxxxxxxxxx', // fs.readFileSync('xxxxx.key')expiresIn: '2d', // https://github.com/vercel/ms},
};

配置结束,接下来分两步走:

  • 对于登录接口,产出token,返回给前端;
  • 对于业务接口,依赖token,做中间件拦截判断鉴权;

先实现第一步,我们只需要在之前的login接口中增加token的逻辑即可。

user.service.ts:

export class UserService {@InjectEntityModel(User)userModal: Repository<User>;@Inject()jwtService: JwtService;@Inject()ctx: Context;async login(options: { username: string; password: string }) {const { username, password } = options;const findRes = await this.userModal.findOne({where: {username,},});if (!findRes) return new httpError.BadRequestError('不存在该用户');const compareRes: boolean = bcryptjs.compareSync(password,findRes.password);if (!compareRes) return new httpError.BadRequestError('密码错误');const token = this.jwtService.signSync({ username });return {accessToken: token,};}
}

当登录成功时,基于用户信息生成加密token,并返回给前端,前端保存在请求头的authorization,接下来每次请求都带给后端。

然后我们封装一个jwt.middleware.ts鉴权中间件,除了登录注册以外依赖个人账号相关的业务接口,都先走到中间件中,代码如下:

import { Inject, Middleware, httpError } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
import { JwtService } from '@midwayjs/jwt';@Middleware()
export class JwtMiddleware {@Inject()jwtService: JwtService;resolve() {return async (ctx: Context, next: NextFunction) => {// 判断下有没有校验信息if (!ctx.headers['authorization']) {throw new httpError.UnauthorizedError();}// 从 header 上获取校验信息const parts = ctx.get('authorization').trim().split(' ');if (parts.length !== 2) {throw new httpError.UnauthorizedError();}const [scheme, token] = parts;if (/^Bearer$/i.test(scheme)) {//jwt.verify方法验证token是否有效await this.jwtService.verify(token, {complete: true,});await next();}};}// 配置忽略认证校验的路由地址public match(ctx: Context): boolean {const ignore = ['/api/login'].includes(ctx.path);return !ignore;}
}

然后在configuration.ts中引入中间件:

import { Configuration, App } from '@midwayjs/core';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as info from '@midwayjs/info';
import { join } from 'path';
import * as orm from '@midwayjs/typeorm';
import * as jwt from '@midwayjs/jwt';
import { JwtMiddleware } from './middleware/jwt.middleware';@Configuration({imports: [koa,validate,{component: info,enabledEnvironment: ['local'],},orm,jwt,],importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {@App()app: koa.Application;async onReady() {// add middlewarethis.app.useMiddleware([JwtMiddleware]);}
}

这样除了中间件内部白名单的接口以外,都会先走到JWT中间件中。

简单测试一下,首先写一个/getShop接口,不在jwt白名单中,首先前端不带token发一次注册请求:

image.png

符合预期,无法访问,被中间件拦下来了。然后我们调一下/login接口,保存一下token,再带着去请求一下/getShop接口:

image.png

OK,至此,midway搭建的登录注册鉴权功能完成。

写在后面

这篇文章对你有帮助的话,非常荣幸。

如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/159727.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务安全-应用协议rsync未授权ssh漏洞复现

目录 服务攻防-应用协议rsync&ssh漏洞复现漏洞复现配置不当-未授权访问-rsync文件备份OpenSSH 用户名枚举漏洞libssh身份验证绕过漏洞 服务攻防-应用协议rsync&ssh漏洞复现 漏洞复现 配置不当-未授权访问-rsync文件备份 rsync默认端口&#xff1a;873 rsync是Linux下…

初识华为云数据库GaussDB for openGauss

01 前言 GaussDB是华为自主创新研发的分布式关系型数据库。该产品具备企业级复杂事务混合负载能力&#xff0c;同时支持分布式事务&#xff0c;同城跨AZ部署&#xff0c;数据0丢失&#xff0c;支持1000的扩展能力&#xff0c;PB级海量存储。同时拥有云上高可用&#xff0c;高可…

PyTorch 深度学习之卷积神经网络(高级篇)Advanced CNN(十)

0. Revision 前面讲的比较简单的是 串行网络结构 1. GoogLeNet 1.1 Inception module w h 要一致 what is 11 convolution? 信息融合-eg.高中各门学科成绩比较(总分) 最主要工作:改变通道数量 why is 11 convolution? 减少10倍 1.2 implementation of inception module 拼…

深度学习实战57-pytorch框架搭建LSTM+CNN模型与实现时间序列的预测过程

大家好,我是微学AI,今天给大家介绍一下深度学习实战57-pytorch框架搭建LSTM+CNN模型与实现时间序列的预测过程, 随着科技的进步,我们越来越依赖数据来理解世界,预测未来。特别是在金融、气候研究、交通管理等领域,时间序列预测已经成为了重要的工具。本文将介绍如何使用L…

算法leetcode|84. 柱状图中最大的矩形(rust重拳出击)

文章目录 84. 柱状图中最大的矩形&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 84. 柱状图中最大的矩形&#xff1a; 给定 n 个非负整…

构建高效问题解答平台:使用Cpolar和Tipas在Ubuntu上搭建专属问答网站

文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4. 公网访问测试5. 结语 前…

KubeVela交付

有什么用我也不想说了&#xff0c;这个是k8s CI/CD,进阶玩家玩的了&#xff0c;比你们喜欢Arg CD更科学&#xff0c;更现代 在 Kubernetes 中安装 KubeVela helm repo add kubevela https://charts.kubevela.net/core helm repo update helm install --create-namespace -n v…

pip快速安装torch、opencv、scipy库

目录 一、pip安装torch 1.1 torch介绍 1.2 torch.nn相关库的导入 1.3win10上torch的安装命令 二、pip安装Opencv 三、pip安装scipy库 一、pip安装torch 1.1 torch介绍 torch的基本功能&#xff1a; ①torch&#xff1a;张量的相关运算&#xff0c;例如&#xff1a;创…

ti am335 RT-LINUX测试

RT-Linux是一个基于Linux内核的实时操作系统&#xff0c;它在满足Linux操作系统的通用性的同时兼顾 实时性能&#xff0c;它的核心是Linux内核的一个实时扩展&#xff0c;它为实时任务提供了必要的调度机制和时间管理。通过采用抢占式调度策略&#xff0c;高优先级的实时任务可…

STM32物联网基于ZigBee智能家居控制系统

实践制作DIY- GC0169-ZigBee智能家居 一、功能说明&#xff1a; 基于STM32单片机设计-ZigBee智能家居 二、功能介绍&#xff1a; 1个主机显示板&#xff1a;STM32F103C最小系统ZigBee无线模块OLED显示器 语音识别模块多个按键ESP8266-WIFI模块&#xff08;仅WIFI版本有&…

Python学习基础笔记七十二——IDE集成开发环境

集成开发环境&#xff0c;英文缩写是IDE。 IDE可以帮你更高效地开发项目代码。因为它提供了非常实用的功能&#xff0c;比如项目文件管理、语法高亮、代码导航、自动补齐代码、语法静态检查、调试、版本控制等等。 两款IDE&#xff1a;Pycharm和VSCode。 pycharm中的代码文件都…

HUAWEI(26)——防火墙双机热备

一、拓扑 二、需求 PC2 ping PC1 FW1与FW2双机热备,FW1为active,FW2为Standby,抢占延时1s VRRP 三、配置 1.IP地址,防火墙接口加入区域 防火墙用户名:admin 防火墙旧密码:Admin@123 防火墙新密码:admin@123 [FW1]interface GigabitEthernet 1/0/0 [FW1-GigabitEthe…

代码随想录Day20 回溯算法 LeetCode77 组合问题

以下内容更详细解释来自于:代码随想录 (programmercarl.com) 1.回溯算法理论基础 回溯法也叫回溯搜索法,是搜索法的一种,我们之前在二叉树中也经常使用到回溯来解决问题,其实有递归就有回溯,有的时候回溯隐藏在递归之下,我们不容易发觉,今天我们来详细介绍一下什么是回溯,它能…

YOLOv5网络结构图

网络结构图&#xff08;简易版和详细版&#xff09; 网络框架介绍 前言&#xff1a; YOLOv5是一种基于轻量级卷积神经网络&#xff08;CNN&#xff09;的目标检测算法&#xff0c;整体可以分为三个部分&#xff0c; backbone&#xff0c;neck&#xff0c;head。 如上图所示…

netca_crypto.dll找不到怎么修复?详细解决办法和注意事项

当你在使用计算机时&#xff0c;突然出现了一个错误提示&#xff1a;“netca_crypto.dll 找不到”。不知道该如何解决这个问题&#xff1f;其实要解决是非常的简单的&#xff0c;今天我们将为你提供几种修复 netca_crypto.dll 找不到的解决方法和一些注意事项。在深入探讨修复方…

ARM day9

src/key_it.c #include "key_it.h" #include "led.h" void key_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);//设置PF9 PF7 PF8GPIO输入//PF9GPIOF->MODER & (~(0x3<<18));//PF8GPIOF->MODER & (~(0x3&l…

进来“抄作业”!示例代码、操作手册,尽在华为云Codelabs!

1 Codelabs 简介 1.1 什么是 Codelabs&#xff1f; Codelabs 是华为云开发者工具&#xff0c;提供互动式的&#xff0c;以实践为主的教程&#xff0c;这些教程旨在指导开发者通过实际操作来学习新的编程技能、工具、框架。华为云 Codelabs 提供丰富的华为云产品代码示例/操…

Redis 集群 Redis 事务 Redis 流水线 Redis 发布订阅 Redis Lua脚本操作

Redis 集群 & Redis 事务 & Redis 流水线 & Redis 发布订阅 Redis 集群linux安装redis主从配置查看当前实例主从信息 Redis Sentinelsentinel Redis Cluster Redis 事务Redis 流水线Redis 发布订阅Redis Lua脚本操作 Redis 集群 linux安装redis 下载安装包&#…

数据结构之手撕链表(讲解➕源代码)

0.引言 我们在学习过顺序表之后&#xff0c;会发现两点不是很优秀的操作&#xff1a; 1.顺序表的头插和中间的插入&#xff1a; 非常麻烦&#xff0c;需要不断的覆盖数据。 2.动态开辟空间&#xff1a; a.一般动态开辟的空间都是以2倍的形式开辟&#xff0c;当…

xml的语法

<!-- 1、每一个xml,有且只有一个根标签&#xff0c;所有xml标签必须写在根标签中 2、标签必须要有合闭 3、xml格式是否正确&#xff0c;可以通过浏览器打开xml。来校验xml格式是否正确 4、xml是区别大小写的 5、xml书写标签名时&#xff0c;不要出现空格等特…