前后端登录逻辑讲解-本文使用RSA加密过程-附代码

news2025/1/16 20:14:25

文章目录

  • 一、项目环境
  • 二、生成公钥私钥
    • 1.生成私钥
    • 2.查看私钥
    • 3.生成公钥
    • 4.查看公钥
  • 三、安装依赖包
  • 四、自测加解密
    • 1.纯前端自测
    • 2.前后端联调
      • ①前端登录代码改为:
      • ②后端登录接口代码:
      • ③验证
  • 五、与数据库密码进行对比(数据库是进行了bcryptjs加密存储的)
  • 六、服务端生成token与token校验
    • 1.服务端安装依赖包
    • 2.配置
    • 3.生成token
    • 4.服务端写个中间件验证token
      • ①编写中间件
      • ②开启中间件
      • ③前端代码编写
      • ④联调自测
  • 七、常见问题答疑(还有其他问题欢迎下方留言笔者进行补充解释):
    • 问题一:为什么登录的时候要进行加密处理?
    • 问题二:为什么存数据库的时候要进行加密处理?

一、项目环境

前端:React
后端:Nodejs(Eggjs)

二、生成公钥私钥

打开你的命令行进行生成公钥与私钥:

1.生成私钥

openssl genrsa -out rsa_1024_priv.pem 1024

在这里插入图片描述

2.查看私钥

cat rsa_1024_priv.pem

在这里插入图片描述

PS:这个私钥需要保存下来放到服务端,到时候解密需要
在这里插入图片描述

3.生成公钥

openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem

在这里插入图片描述

4.查看公钥

cat rsa_1024_pub.pem

在这里插入图片描述

PS:你可以把这个公钥copy到前端代码中,也可以copy到后端代码中然后通过接口暴露出来(推荐后者,原因是后续如果公钥和私钥要变化的时候,然后用公钥的地方就无需变动了,还是正常接口获取即可,否则就得每个地方把公钥替换一遍)

三、安装依赖包

PS:
前端需要安装的是:jsencrypt
后端需要安装的是:node-jsencrypt

// 前端的代码安装这个
npm install jsencrypt -S

// 后端的代码安装这个
npm install node-jsencrypt -S

四、自测加解密

1.纯前端自测

我们先把公钥私钥都放到前端,加解密也都在前端进行处理,看是否与我们预期一致。

前端登录的提交代码如下:

import JSEncrypt from 'jsencrypt';

/** 这里只写了方法里面的具体内容哈 */
// 加密
const publicKey = '这里放你的公钥';
console.log('吴迪想看的:', values);
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
const encrypted = encrypt.encrypt(values.password);
console.log('加密后: ', encrypted);

// 解密
const privateKey = '这里放你的私钥';
const decrypt = new JSEncrypt();
decrypt.setPrivateKey(privateKey);
const uncrypted = decrypt.decrypt(encrypted as string);
console.log('解密后:', uncrypted);

在这里插入图片描述

前端测试代码逻辑没问题了之后就可以把服务端代码移植过去了

2.前后端联调

①前端登录代码改为:

const handleSubmit = async (values: API.LoginParams) => {
  try{
    // 把密码加密登录
    const publicKey = '替换你的公钥';
    const encrypt = new JSEncrypt();
    encrypt.setPublicKey(publicKey);
    const encrypted = encrypt.encrypt(values.password as string || '');
    // 调用你的登录接口然后处理参数
    const res = await login({
      ...values,
      password: encrypted as string,
    });
    // 后续自行根据返回值做成功和失败处理
  } catch (error) {
    // 错误处理
  }
};

②后端登录接口代码:

import { Controller } from 'egg';
const JSEncrypt = require('node-jsencrypt');

module.exports = app => {
  return class UserController extends Controller {
    public async login() {
      const privateKey = '替换你自己的私钥'
      const { ctx } = this;
      const data = ctx.request.body;

      // 解密
      const decrypt = new JSEncrypt();
      decrypt.setPrivateKey(privateKey);
      const uncrypted = decrypt.decrypt(data.password as string);
      console.log('解密后:', uncrypted);
      // TODO:数据库处理

      ctx.body = await ctx.service.test.sayHi(data.username);
    }
  }
};

③验证

当我们发现调接口的时候需要验证俩个地方
1.浏览器上的network中的参数被加密了
在这里插入图片描述

2.然后后端日志输出的是自己输入的密码的时候就对了
在这里插入图片描述

五、与数据库密码进行对比(数据库是进行了bcryptjs加密存储的)

import { Controller } from 'egg';
const JSEncrypt = require('node-jsencrypt');
const bcrypt = require('bcryptjs');

// 判断密码是否输入正确
const comparePassword = (userInputPassword: string, enCodePassword: string) => {
    return bcrypt.compareSync(userInputPassword, enCodePassword);
};

// 解密RSA加密
const deRSACode = (password: string, privateKey: string) => {
    const decrypt = new JSEncrypt();
    decrypt.setPrivateKey(privateKey);
    return decrypt.decrypt(password);
}

module.exports = app => {
  return class UserController extends Controller {
    public async login(ctx) {
      const data = ctx.request.body;
      // 这里第二个参数自行替换你的私钥
      const uncrypted = deRSACode(data.password as string, app.config.privateKey);
      // 数据库处理
      // 这里替换成你自己的数据库表名和字段名
      const userInfo = await app.mysql.get('user', { login_name: data.username });
      console.log('userInfo: ', userInfo);
      if (!userInfo) {
        return ctx.failure("用户名或密码错误!");
      }
      if (userInfo) {
        // 第一个参数是用户输入的密码,第二个参数是数据库中加密的密码
        const isPasswordRight = comparePassword(uncrypted, userInfo.password);
        if (!isPasswordRight) {
		  // 报错处理:用户名或密码错误!
        }
      }
      // 成功处理:登录成功
    }
  }
};

六、服务端生成token与token校验

1.服务端安装依赖包

npm install egg-jwt --save

2.配置

找到 config\plugin.ts 文件进行补充:

jwt: { //jwt插件启用
  enable: true,
  package: 'egg-jwt',
}

在这里插入图片描述


找到 config\config.default.js 文件在进行补充:

jwt: { //jwt加盐以及设置过期时间
  secret: 'jwt',
  expiresIn:'1h'
}

在这里插入图片描述

3.生成token

在你自己需要生成token的controller里面写:

/**
 * 生成token
 * 第一个参数是存储参数
 * 第二个参数是加盐
 * 第三个参数是配置,比如过期时间
 * */
const options = {
  expiresIn: app.config.jwt.expiresIn,
};
const token = ctx.app.jwt.sign(
  { ...userInfo },
  app.config.jwt.secret,
  options
);
console.log('token: ', token);

在这里插入图片描述

4.服务端写个中间件验证token

①编写中间件

我们新建个文件:app\middleware\jwtValidate.ts

module.exports = (_, app) => {
    return async function (ctx, next) {
        // 设置跳过验证的token的路由白名单,比如笔者把登录接口跳过了
        const routerAuth = ['/api/login/account'];
        // 获取当前路由
        const url = ctx.url;
        // 判断当前路由是否需要跳过验证token
        if (routerAuth.includes(url)) {
            await next();
        } else {
            // 获取token
            var token = ctx.headers.token ? ctx.headers.token : '';
            console.log("获取token", token)
            // 解析token
            const throwError = () => {
                ctx.body = {
                    code: 401,
                    errorMessage: 'token失效或解析错误',
                    data: null
                };
            };
            try {
                const decode = await app.jwt.verify(token, app.config.jwt.secret);
                console.log("解析出来的数据:", decode)
                if (decode) {
                    await next();
                } else {
                    throwError();
                }
            } catch (err) {
                // app.logger.debug(err); //打印错误日志
                throwError();
            }
        }
    }
}

②开启中间件

找到文件 config\config.default.ts 启用中间件

config.middleware = ["jwtValidate"];

在这里插入图片描述

③前端代码编写

一般来讲都是登录之后后端返给前端token,然后前端后面的每次接口调用都将该token带到请求头里面

登录逻辑代码:

localStorage.setItem('token', res?.result?.token || '');

登录成功之后把token存到storage里面
在这里插入图片描述
请求拦截代码:

// 请求拦截器
requestInterceptors: [
  (config: RequestOptions) => {
    // 拦截请求配置,进行个性化处理。
    const headers = { ...config.headers, token: localStorage.getItem('token') };
    return { ...config, headers };
  },
],

将token拼到请求头当中
在这里插入图片描述

④联调自测

调用接口看前端token是否传过来了,token与存储的内容是否正确解析。
在这里插入图片描述

PS: token过期时间可以设置短点,把token过期也自测一下

七、常见问题答疑(还有其他问题欢迎下方留言笔者进行补充解释):

问题一:为什么登录的时候要进行加密处理?

答:
可以使用户的密码不被泄露,即使可能在我们系统还是会受到重放攻击,但是不会将用户的原始密码暴露出去。
暴露出用户的原始密码可能会导致不法分子用该密码去尝试登录用户的其他平台(因为大部分人把所有软件设置的密码都大差不差甚至一样)。

问题二:为什么存数据库的时候要进行加密处理?

答:
因为保证内部人员或者数据库被盗之后不会对用户的损失过重。

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

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

相关文章

关于非授权访问的逻辑漏洞挖掘

简介 挖洞的时候日常笔记,很多细节都写得不好,师傅们不要介意 开始 获取目标:https://fofa.info/ 在fofa上输入body“后台登录”,可以看到一大堆目标,今天我打算去测试逻辑漏洞 进入目标地址 随意输入用户名和密码&…

Python LC Loan贷款数据集 统计分析 数据挖掘 研究报告

实验代码:https://download.csdn.net/download/Amzmks/87396462 首先读表 将有空值的列和完全相同的列删除 将数值型数据单独挑出来 将数值型数据从string转为float 用方差阈值法筛选特征较为明显的部分数值型数据 将文本型数据单独挑出来 去除所有的可能的头…

计算机存储系统

前言何为存储系统?存储系统是指计算机中由存放程序和数据的各种存储设备、控制部件及管理信息调度的设备(硬件)和算法(软件)所组成的系统。为何需要存储系统?信息是人类认知外界的方式,最初的信…

对CAS的理解

CAS的概念(compare and swap):比较并交换我们首先要明白,自旋是一个纯用户态的操作,本身并没有进行加锁,所执行的代码,是在并发环境下执行的,没有锁。我们举一个例子来说明自旋加CAS的作用&…

Day05 C++STL入门基础知识三——String容器(上)概念-构造-赋值-拼接-查找-替换【全面深度剖析+例题代码展示】

文章目录1. 基本概念1.1 本质1.2 string与char*的区别1.3 特点2. 构造函数2.1 四种构造2.2 举例展示3. 赋值操作3.1 七种赋值函数原型(operator等号赋值/assign成员函数赋值)3.2 举例展示4. 拼接操作4.1 七种拼接函数原型4.2 举例展示5. 查找操作5.1 八种…

分布式概念

目录一、什么是分布式二、分布式与集群的关系三、软件架构演变四、RPC是什么1.RPC思想原理2.服务之间的交互可以用两种方式五、分布式思想与基本概念1.高并发2.高可用3.注册中心4.负载均衡5.服务雪崩6.熔断7.限流8.API网关9.服务跟踪10.弹性云一、什么是分布式 《分布式系统原…

Python流程控制语句之循环语句

上一篇:Python流程控制语句之选择语句 文章目录前言一、while 循环二、for 循环1. 进行数值循环2. 遍历字符串三、循环嵌套总结前言 生活中很多问题都无法一次解决,就像谚语所说:罗马不是一天建成的。一些事物必须周而复始地运转才能保证其存…

linux并发控制详解

目录 1.并发控制 1.1.并发概念 1.2.并发问题 2.多CPU核心 3.解决 4.中断屏蔽 5.原子操作 6.自旋锁 7.自旋锁衍生读写自旋锁 7.1.自旋锁与读写自旋锁的对比: 8.读写自旋锁衍生顺序锁 9.RCU 10.信号量 11.互斥体(互斥锁) 11.1.互…

2023起点上,一段迷茫的自我倾诉

大家新年快乐。 回顾记忆中渐渐远去的2022。 我曾想象随着一年过去我就能取得很大的进步,“彻底”改变自己的生活状态。其实不过幻想罢了,人才不会无缘无故进步呢。 我曾一度沉溺于网络世界中的关注,想象着自己将可以取得一些成就&#xff…

【每日一道智力题】三个火枪手(快来看人生哲理)

🚀write in front🚀 📜所属专栏: 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是对我最大的激励…

Qt 之 QSystemTrayIcon

文章目录一、QSystemTrayIcon是什么二、属性三、公共类型四、信号提示:以下是本篇文章正文内容,下面案例可供参考 一、QSystemTrayIcon是什么 QSystemTrayIcon类为应用程序在系统托盘中提供一个图标。 如下图: 现代操作系统通常在桌面上提…

“深度学习”学习日记。与学习有关的技巧--Bacth Normalization

2023.1.25 现在已经学习过了,如果我们设置了合适的权重初始值,则各层的激活值分布会呈现适当的广度,从而可以时神经网络模型顺利的进行学习。 而 batch normalization算法 的思想就是为了使得各层有适当的广度,“强制性”地调整…

No package ‘vips‘ found系列问题解决方案

目录 系列报错集合 错误1 错误2 错误3 解决方案清单 系列报错集合 错误1 No package vips found Package vips was not found in the pkg-config search path. Perhaps you should add the directory containing vips.pc to the PKG_CONFIG_PATH environment variable N…

医疗实体及关系识别挑战赛

赛题概要 请本赛题排行榜前10的队友通过作品提交源代码,模型以及说明文档,截止时间为09/27/23:59:59.若文件过大,可发送至官网邮箱:AICompetitioniflytek.com。若截止时间内未提交,官方回通过电话联系相关选手&#x…

JavaEE8-Bean的生命周期

目录 1.Bean执行原理分析 2.Bean生命周期 2.1.实例化Bean:为Bean分配内存空间。(相当于买房,从无到有) 2.2.设置属性:Bean注入和装配。(执行依赖类的注入:A需要使用B的方法,先初…

win32com操作word API精讲 第六集 Range(四)对齐和缩进

本课程《win32com操作word API精讲&项目实战》同步在B站、今日头条、视频号及本公众号发布。其中本平台以发布文字教程为主,所有平台ID均为:一灯编程 今天是大年初二,一灯在此祝愿各位朋友兔年吉祥,达成所想。 本节课主要讲解…

机器学习(六):模型评估

文章目录 模型评估 一、分类模型评估 二、 回归模型评估 三、拟合 1、欠拟合 2、过拟合 模型评估 模型评估是模型开发过程不可或缺的一部分。它有助于发现表达数据的最佳模型和所选模型将来工作的性能如何。 按照数据集的目标值不同,可以把模型评估分为分类…

Python信用卡欺诈检测 [TensorFlow]

Python信用卡欺诈检测 [TensorFlow] 提示:前言 Python 信用卡欺诈检测 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录Python信用卡欺诈检测 [TensorFlow]前言一、导入包二、加载数据三、加载数据四、 …

WebAssembly编译之(1)-asm.js及WebAssembly原理介绍

WebAssembly介绍及产生历程 1、什么是WebAssembly、为什么WASM? 我们知道Web的应用几乎涵盖了大半个互联网应用;越多越多的Web应用层出不穷,而然Web最致命的劣势就是其在浏览其的运行效率特忙,尤其是web游戏的体验不佳。 而Web…

ubuntu安装Espeak实现tts文字转语音

目录参考一、介绍二、安装安装包安装查看安装版本和espeak-data路径直接尝试安装中文包三、生产wav文件四、代码引入参考 ubuntu完美安装espeak支持中文和粤语 不再报错:Full dictionary is not installed for ‘zh’ 一、介绍 **用途:**可识别多语言的朗读软件 …