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

news2024/11/23 11:23:31

请求体

这里遇到了个问题,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 ', '');
  // 验证token
  try {
    // 获取到用户名,用户名是唯一的,比如手机号,
    // 要保证请求每一个接口时都用用户名这个参数,这里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/jsonwebtoken
    switch (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.coloradmin.cn/o/1057516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

专业PDF编辑阅读工具PDF Expert mac中文特点介绍

PDF Expert mac是一款专业的PDF编辑和阅读工具。它可以帮助用户在Mac、iPad和iPhone等设备上查看、注释、编辑、填写和签署PDF文档。 PDF Expert mac软件特点 PDF编辑:PDF Expert提供了丰富的PDF编辑功能,包括添加、删除、移动、旋转、缩放、裁剪等操作…

基于Java的老年人体检管理系统设计与实现(源码+lw+部署文档+讲解等)

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

JDBC学习笔记(1)

连接数据库 下载mysql-connector-java,这里我是看的这个连接mysql-connector-java下载。 下载后并且导入了Idea中的lib文件下。 导入成功后,为了验证可以通过CTRLn来搜索Driver看看有没有添加进来。 随后在MySQL中创建一个数据库,我这里直…

小狐狸ChatGPT付费创作系统V2.0.4智能问答小程序,修复一个pc版的bug

狸GPT付费体验系统是一款基于ThinkPHP框架开发的AI问答小程序,是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。 当前全民热议ChatGPT,流量超级大,引流不要太简单!一键下单即可拥有自己的GPT!无限多开、免费更新不…

Vue中如何进行多语言处理

Vue中的多语言处理 在开发多语言Web应用程序时,处理文本翻译和国际化是一个重要的任务。Vue.js提供了多种方法来实现多语言处理,以确保您的应用程序能够支持不同语言的用户。本文将深入探讨在Vue中进行多语言处理的方法,并提供示例代码来帮助…

基于SpringBoot的高校实习管理系统设计与实现(亮点:多角色、功能全、进可攻退可守)

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

2023年中国家用智能投影市场发展趋势分析

2023年上半年,连续多年保持高速增长态势的家用智能投影行业,首次半年度出现同比负增长态势。2023年上半年,中国家用智能投影市场全渠道推总销量为323.0万台,同比下降2.6 %;销售额61.0亿元,同比下降2.9%&…

SLAM面试笔记(7) — Linux面试题

目录 问题1:Linux系统基本组件? 问题2:Linux和Unix有什么区别? 问题3:Linux下编译程序 问题4:gcc基本格式和常用指令 问题5:用什么命令查找内存和交换使用情况? 问题6&#xf…

基于SpringBoot的反诈宣传平台设计与实现(源码+lw+部署文档+讲解等)

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

第四范式在港交所主板正式挂牌上市

9月28日上午9时30分,中国最大的以平台为中心的决策类人工智能公司第四范式,正式于香港联合交易所挂牌上市,股份代号为6682.HK。 作为一家长期专注于技术创新和企业端落地的人工智能软件公司,第四范式致力将AI应用到各行各业&#…

linux每处理器内存分配

一、每处理器 在多处理器系统中,每处理器变量为每个处理器生成一个变量副本,每个处理器访问自己的副本; 优点:避免处理器之间和处理器缓存之间的同步,提高程序的执行速度。 二、编程接口 1、静态 DEFINE_PER_CPU(ty…

Vue中如何进行数据请求拦截与错误处理

当你在Vue.js中开发应用程序时,数据请求拦截和错误处理是不可或缺的一部分。通过拦截请求,你可以在发送请求之前对其进行修改,而通过错误处理,你可以有效地处理来自服务器的错误响应。本文将介绍如何在Vue.js中进行数据请求拦截和…

马铃薯甲虫的成虫和幼虫数据集(YOLO检测)

数据集下载(不要积分):https://download.csdn.net/download/qq_40840797/88389331 这是一个包含1810张图像的数据集,图像中展示了马铃薯甲虫的成虫和幼虫。这个数据集是专门为了用于农业机器人的点对点喷洒而设计的。这些图像是在…

云原生数据库TDSQL-C

数据库系统核心模块 云原生数据库要解决什么问题? HTAP 云数据库VS云原生数据库

Debezium日常分享系列之:使用数据库中的数据流进行在线机器学习

Debezium日常分享系列之:使用数据库中的数据流进行在线机器学习 一、背景介绍二、数据集准备三、使用 Apache Flink 进行分类四、使用 Debezium 和 Kafka 作为源数据流五、构建 Flink 流 k-means六、评估模型七、使用 Apache Spark 进行分类八、定义数据流九、定义和…

JAVA在线电子病历编辑器源码 B/S架构

电子病历在线制作、管理和使用的一体化电子病历解决方案,通过一体化的设计,提供对住院病人的电子病历书写、保存、修改、打印等功能。电子病历系统将临床医护需要的诊疗资料以符合临床思维的方法展示。建立以病人为中心,以临床诊疗信息为主线…

开源白板工具 Excalidraw 架构解读

本文讲解开源白板工具 Excalidraw 的架构设计。 版本 0.16.1 技术栈 Vite React TypeScript Yarn Husky。 脚手架原来是用的是 Create React App,但这个脚手架已经不维护了,一年多没发布新版本了。 目前市面上比较流行的 React 脚手架是 Vite&…

pycharm一直没显示运行步骤,只是出现waiting for process detach

pycharm一直没显示运行步骤,只是出现waiting for process detach;各类音乐免费软件;最棒的下载torch-geometric-CSDN博客(不太推荐)我强烈推荐这个:_waiting for process detachhttps://blog.csdn.net/weix…

APScheduler框架使用

目录 概述架构重要概念Job 作业Trigger 触发器Executor 执行器JobstoreEvent 事件调度器 工作流程使用 概述 APScheduler(advanceded python scheduler)基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分…

网络层常见协议——IPV4、IPV6、ARP、ICMP、QoS

目录 1、IPV4 协议 IPV4 地址的组成: IPV4地址的分类: 关于多播和组播: 常见组播地址分类: 特殊的 IPV4 地址: 私有地址和公有地址: 私有地址的范围: 子网划分: 子网掩码&…