Koa学习4:密码加密、验证登录、颁发token、用户认证

请求体

这里遇到了个问题,ctx.request.body 的值是一个字符串。明明已经使用了koa-body中间件

查了一下原因是:

ctx.request.body的值可能是一个对象或一个字符串,取决于请求的Content-Type和请求体的格式。
当使用koa-body中间件时,它会根据请求的Content-Type自动解析请求体,并将解析后的结果存储在ctx.request.body中。如果Content-Type是application/json,则koa-body将解析请求体为JSON格式,并将其存储为对象;如果Content-Type是application/x-www-form-urlencoded,则koa-body将解析请求体为键值对形式,并将其存储为对象;如果Content-Type是multipart/form-data,则koa-body将解析请求体为多部分表单数据,并将其存储为对象。
但是,如果没有使用koa-body中间件或者请求的Content-Type不是以上几种类型,ctx.request.body将保持为原始的字符串形式。在这种情况下,您需要根据请求的内容类型进行手动处理。

因此请求参数要设置成json格式
在这里插入图片描述

密码加密

这里使用bcryptjs

bcryptjs是一个JavaScript库,用于将密码哈希化并进行比较。它使用bcrypt算法,这是一种密码哈希函数,可以将密码转换为不可读的字符串,以增加安全性。bcryptjs库提供了一种简单的方法来使用bcrypt算法,以便在应用程序中存储和比较密码。它还提供了一些其他功能,例如生成随机的salt(盐)值,以增加哈希的安全性。在Web应用程序中,使用bcryptjs可以保护用户密码,防止黑客攻击和数据泄露。

安装

npm install bcryptjs

这里将密码加密抽离成一个中间件,便于后期的维护。如果需要更换加密方式的时候,只需要使用一个新的中间件即可,不会对原有代码产生大的改动。

编写中间件
user.middleware.js

// 密码加密
const crpytPassword = async (ctx, next) => {const requestBidy = ctx.request.body;if (!requestBidy?.password) {console.error('密码为空,密码加密失败');return;}// 加盐const salt = bcrypt.genSaltSync(10);// 哈希加密const hash = bcrypt.hashSync(requestBidy.password, salt);// 更新密码ctx.request.body.password = hash;console.log('加密后的密码:', ctx.request.body);await next();
};

使用中间件,在进行注册之前将密码进行加密
user.route.js

// 注册
router.post('/register', userRegisterValidator, crpytPassword,register);

在这里插入图片描述

验证登录

user.middleware.js中新定义一个中间件,用来校验登录

// 用户登录校验
const userLoginValidator = async (ctx, next) => {// 获取入参const requistBody = ctx.request.body;// 合法性判断if (!requistBody.user_name || !requistBody.password) {// console.error 打印的内容会被记录到服务器的日志里console.error('用户名或密码为空:', requistBody);//使用了Koa的错误处理机制ctx.app.emit('error', UserErr.userFormatError, ctx);return;}// 使用try catch 避免进行数据库操作时出现问题,导致程序异常try {// 判断数据库中是否存在该用户,若存在还需比较密码是否正确// 1、判断用户是否存在,不存在则提示用户进行注册const user = await getUserInfo({ user_name: requistBody.user_name });if (!user) {console.error('用户名不存在', requistBody.user_name);ctx.app.emit('error', UserErr.userNameErr, ctx);return;}// 2、用户存在,验证密码是否正确if (!bcrypt.compareSync(requistBody.password, user.password)) {console.error('密码不正确', requistBody.password);ctx.app.emit('error', UserErr.passwordErr, ctx);return;}// 验证通过后交由一个中间件处理} catch (error) {console.error('用户登录失败:', error);return ctx.app.emit('error', UserErr.userLoginErr, ctx);}await next();
};

user.err.type.js添加新的错误类型

userNameErr:{code:'10004',message:'用户名不存在,请进行注册',data:null
},
passwordErr:{code:'10005',message:'密码错误,请确认',data:null
},
userLoginErr:{code:'10006',message:'用户登录失败',data:null
}

user.route.js中添加中间件

// 登录
router.post('/login', userLoginValidator, login);

颁发token

登录成功后给用户颁发一个token,用户在以后的每一次请求中都会携带token,服务端会对进行校验。这里我们使用JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式来在各方之间安全地传输信息。JWT 通常用于身份验证和授权场景,它可以在客户端和服务器之间传递信息,以验证用户的身份和授权用户访问资源。

在 Node.js 中,我们可以使用第三方库 jsonwebtoken 来生成和验证 JWT。生成 JWT 时,我们需要提供一个包含用户信息的 JSON 对象和一个密钥,然后将其编码为一个字符串。验证 JWT 时,我们需要提供这个字符串和相同的密钥,然后解码出 JSON 对象,从而获取用户信息。

JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部包含了 JWT 的类型和使用的算法,载荷包含了用户信息和其他元数据,签名用于验证 JWT 的完整性和真实性。

安装

npm install jsonwebtoken

颁发token
user.controller.js

Token通常会记录一些用户身份验证信息,例如用户ID、角色、权限等。但是,用户密码通常不会记录在Token中,因为这样做会增加安全风险。相反,用户密码通常会在用户登录时进行验证,然后在服务器端进行加密存储。在后续的请求中,服务器会使用Token来验证用户身份,而不是使用密码。这样可以保护用户密码的安全性。

// 导入jsonwebtoken
const jwt = require('jsonwebtoken');// 登录async login(ctx, next) {// 1、获取数据const requistBody = ctx.request.body;try {// 2、获取用户信息(在token的paykoad中要记录需要用到的用户信息,比如:id、user_name、is_vip)// 为了保证安全,token里不能记录密码const { password, ...res } = await getUserInfo({user_name: requistBody.user_name,});ctx.body = {code: 0,message: '登录成功',data: {// 用户信息和私钥token: jwt.sign(res, 'test', {expiresIn: '2h', // 过期时间2小时}),},};} catch (error) {console.error('用户登录失败', error);}}

在这里插入图片描述

token验证

当用户登录成功后,在访问某些功能时需要判断用户是否有这个权限,这时可以通过解析token拿到用户数据来进行权限的判断。
下面以修改用户密码为例,来实现token的验证

将token添加到请求头中

参数名选择:Authorization,参数值为登录成功后返回的token值。我这里使用的软件是Apifox,前面介绍过就不说了。
在这里插入图片描述
注意:参数值里不要在外面加上引号

创建处理用户鉴权的中间件
后续的大部分操作都会进行权限验证,判断是否有该功能的权限。判断权限需要处理token,这里使用统一的中间件进行处理
auth.middleware.js

// 导入jwt
const jwt = require('jsonwebtoken');
// 导入工具函数
const { createSecretKey } = require('../tool/tool');
// 导入错误类型
const { AuthErr } = require('../constant/err.type');const auth = async (ctx, next) => {// 获取token信息const { authorization } = ctx.request.header;// token信息通常放在请求头的Authorization属性下,并且在请求头中,可以使用Bearer前缀来标识const token = authorization.replace('Bearer ', '');// 验证tokentry {// 获取到用户名,用户名是唯一的,比如手机号,// 要保证请求每一个接口时都用用户名这个参数,这里ctx.request.body返回的是一个字符串const requistBody = JSON.parse(ctx.request.body);// 私钥let key = createSecretKey(requistBody.user_name);// 校验,校验成功后会解析出用户信息const userInfo = jwt.verify(token, key);// 将其挂载到ctx.state下,方便后续使用ctx.state.userInfo = userInfo;} catch (error) {// 错误信息见 https://www.npmjs.com/package/jsonwebtokenswitch (error.name) {// token过期case 'TokenExpiredError':console.log('token已过期', error);return ctx.app.emit('error', AuthErr.tokenExpiredError, ctx);//   token验证错误case 'JsonWebTokenError':console.log('token验证失败', error);return ctx.app.emit('error', AuthErr.jsonWebTokenError, ctx);//   其他异常default:console.error('鉴权验证失败', error);return ctx.app.emit('error', AuthErr.authVerifyError, ctx);}}await next();
};module.exports = {auth,
};

在路由里添加修改路由

user.route.js

// 修改密码
router.post('/editUserInfo', auth, editUserInfo);

在这里插入图片描述
在这里遇到了一个问题,就是token一直验证失败;后来使用jwt.decode(token);进行token解析,结果一直失败。定位到问题在最开始的生成token中,后来发现是由于这两个时间导致的。如果需要用的时间的话,最好通过dayjs之类的库,对时间进行格式化。

数据库中存储的时间是按照特定的格式进行存储的,而在请求返回时,服务器会将时间转换为ISO 8601格式的字符串,这是一种通用的时间格式。

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

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

相关文章

微信小程序 movable-area 区域拖动动态组件演示

movable-area 组件在小程序中的作用是用于创建一个可移动的区域,可以在该区域内拖动视图或内容。这个组件常用于实现可拖动的容器或可滑动的列表等交互效果。 使用 movable-area 组件可以对其内部的 movable-view 组件进行拖动操作,可以通过设置不同的属…

mp4视频太大怎么压缩变小?

mp4视频太大怎么压缩变小?确实,很多培训和教学都转向了线上模式,这使得我们需要下载或分享大量的在线教学视频。然而,由于MP4视频文件通常较大,可能会遇到无法打开或发送的问题。为了解决这个问题,我们可以…

selenium-webdriver-Chrome新驱动地址(Chrome115及以上版本)

Chrome115、Chrome116、Chrome117,在旧的链接并没有 新地址:https://googlechromelabs.github.io/chrome-for-testing/ 参考学习链接(我也是根据这个老师的链接学到的):https://www.cnblogs.com/wuxianfeng023/p/1765…

阿里云 linux tomcat 无法访问方法

1、阿里云放行tomcat端口 例如7077端口号 2、linux 命令行防火墙 设置端口打开 以下命令查看是否开启指定端口 firewall-cmd --list-ports以下命令添加指定端口让防火墙放行 firewall-cmd --zonepublic --add-port3306/tcp --permanent以下命令重新启动防火墙 systemctl re…

《Python 自动化办公应用大全》书籍推荐(包邮送书五本)

前言 随着科技的快速发展和智能化办公的需求增加,Python自动化办公成为了一种趋势。Python作为一种高级编程语言,具有简单易学、功能强大和开放源代码等优势,可以帮助我们更高效地完成日常办公任务。 Python自动化办公还可以帮助我们实现更…

HarmonyOS/OpenHarmony原生应用开发-华为Serverless云端服务支持说明(一)

云端服务的实现是HarmonyOS/OpenHarmony原生应用开发的一个重要的环节,如果用户端是鸿蒙原生应用,但是服务端即云端还是基于传统的各种WEB网络框架、数据库与云服务器,那么所谓的原生应用开发实现的数据即后端服务是和以前、现在的互联网、移…

腾讯云/阿里云国际站免费账号:腾讯云国际站如何对象存储cos设置防盗链

简介 为了避免恶意程序使用资源 URL 盗刷公网流量或使用恶意手法盗用资源,腾讯云国际站给用户带来不必要的损失。腾讯云对象存储支持防盗链配置,建议您通过控制台的防盗链设置配置黑/白名单,来进行安全防护。 注意: 如果您访问对…

mysql 物理备份及恢复

一、物理复制的基本概念 物理备份:直接复制数据库文件,适用于大型的数据库环境,不受存储引擎的限制,但不能恢复到不同的mysql版本 完整备份:也叫完全备份,每次将所有数据(不管自第一次备份有没有修改过&…

c++ 基础知识(一)

文章目录 1. C关键字 2. 命名空间 3. C输入&输出 4. 缺省参数 文章内容 1. C关键字(C98) C总计63个关键字,C语言32个关键字 ps:下面我们只是看一下C有多少关键字,不对关键字进行具体的讲解。后面我学了以后再细讲。 2. 命名空间 …

Holographic MIMO Surfaces (HMIMOS)以及Reconfigurable Holographic Surface(RHS)仿真

这里写目录标题 Simulation setupchatgpt帮我总结代码总结:chatgpt生成的代码还是不靠谱:考虑把之前看的RHS中对于多用户的改成单用户全系MIMO与普通MIMO或者说RIS的区别到底是啥? Holographic MIMO Surfaces (HMIMOS)…

微信小程序--》从模块小程序项目案例23.10.09

配置导航栏 导航栏是小程序的门户,用户进来第一眼看到的便是导航栏,其起着对当前小程序主题的概括。而我们 新建的小程序 时,第一步变开始配置导航栏。如下: 配置tabBar 因为配置tabBar需要借助字体图标,我这里平常喜…

【数据结构】计数排序 排序系列所有源代码 复杂度分析(终章)

目录 一,计数排序 1,基本思想 2,思路实现 3,计数排序的特性总结: 二,排序算法复杂度及稳定性分析 三,排序系列所有源代码 Sort.h Sort.c Stack.h Stack.c 一,计数排序 计数…

厌烦了iPhone默认的热点名称?如何更改iPhone上的热点名称

你对你默认的热点名称感到厌倦了吗?这篇文章是为你准备的。在这里,你可以了解如何轻松更改iPhone上的热点名称。 个人热点会将你的手机数据转换为Wi-Fi信号。手机上的个人热点使用户能够与其他用户共享其蜂窝数据连接。当你在WIFI网络之外时&#xff0c…

使用华为eNSP组网试验⑹-组建基于BGP的网络

BGP(Border Gateway Protocol -- 边界网关协议)是一种在自治系统之间动态交换路由信息、具有丰富的路由控制机制、稳定而安全的路由协议,一般部署在骨干(主要、核心)路由器。 BGP适用于大中型网络的组建,在很多企业当中都有应用。 一般情况下&#xff0c…

网关、网桥、路由器和交换机之【李逵与李鬼】

概念 网关 网关简单来说是连接两个网络的设备,现在很多局域网都是采用路由器来接入网络,因此现在网关通常指的就是路由器的IP。网关可用于家庭或者小型企业,连接局域网和Internet,也有用于工业应用的。 网桥 网桥也叫桥接器,是连接两个局域网的一种存储/转发设备,它能…

Linux搭建我的世界MC服务器 【Minecraft外网联机教程】

目录 前言 1. 安装JAVA 2. MCSManager安装 3.局域网访问MCSM 4.创建我的世界服务器 5.局域网联机测试 6.安装cpolar内网穿透 7. 配置公网访问地址 8.远程联机测试 9. 配置固定远程联机端口地址 9.1 保留一个固定tcp地址 9.2 配置固定公网TCP地址 9.3 使用固定公网…

基于Java的在线文档编辑管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

上门按摩小程序|同城上门按摩软件开发|上门按摩系统;

上门按摩小程序的开发具有许多优势,下面就给大家介绍下按摩小程序功能: 上门按摩小程序的优势 方便快捷:上门按摩小程序提供在线预约服务,用户可以通过手机随时随地预约按摩师上门服务,避免了传统预约方式的繁琐和不确定性。 个性…

数据结构-二叉查找树(BST)

二叉查找树 需要满足这些规则: 左子节点小于父节点右子节点大于父节点 查找的效率 非常好,每次都能根据大小去舍弃另一半的分支,极大的减少的比对次数 具体的性能,取决于树的层数和平衡程度。 BST树的节点 struct Node {No…

qt5.14.2+VS源码调试记录

在对qt使用时,有时需要对源代码进行调试,方便进行问题定位和debug,但直接安装的qt不能进入qt源码,需要进行一定的操作才能进行源码调试和定位。 源码调试需要对应版本的pdb文件,可以在官网下载,也可找其它…