rsa加密登录解决方案
1.问题
账密登录方式中用户输入密码后,把账号、密码通过http传输到后端进行校验,然而密码属于敏感信息,不能以明文传输,否则容易被拦截窃取,因此需要考虑如何安全传输密码
2.解决方案
使用rsa加密方式,rsa属于非对称加密,特点就是公钥加密私钥解密
2.1后端生成公钥私钥
生成公私钥,把公钥返回给前端,私钥用redis缓存
ManagerController.java
@GetMapping("/key")public ResponseEntity<ApiResponse> key(@RequestParam("loginNo") String loginNo) {String key = managerService.generateKey(loginNo);return ApiResponse.success(key);}
ManagerServiceImpl.java
@Overridepublic String generateKey(String loginNo) {QueryWrapper<Manager> wrapper = new QueryWrapper<>();wrapper.eq("loginNo", loginNo);Manager entity = this.getOne(wrapper);if (Objects.isNull(entity)) {throw new CodeException("用户不存在:loginNo=" + loginNo);}try {KeyPair keyPair = RSAUtil.generateKeyPair();String publicKey = RSAUtil.getPublicKey(keyPair);String privateKey = RSAUtil.getPrivateKey(keyPair);log.info("publicKey={}", publicKey);log.info("privateKey={}", privateKey);String redisKey = RedisKey.MANAGE_LOGIN_RSA_PRIVATEKEY + "$" + loginNo;// 清除缓存redisService.del(redisKey);// 私钥添加到缓存redisService.set(redisKey, privateKey, 5, TimeUnit.MINUTES);return publicKey;} catch (Exception e) {log.error("生成rsa密钥失败", e);}return null;}
RSAUtil.java
public class RSAUtil {private static final Charset CHARSET = StandardCharsets.UTF_8;private static final String ALGORITHM = "RSA";/*** 生成密钥对* @return* @throws NoSuchAlgorithmException*/public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);keyPairGenerator.initialize(1024);return keyPairGenerator.generateKeyPair();}/*** 生成公钥* @param keyPair* @return*/public static String getPublicKey(KeyPair keyPair) {PublicKey publicKey = keyPair.getPublic();byte[] bytes = base64Encode(publicKey.getEncoded());return new String(bytes, CHARSET);}/*** 生成私钥* @param keyPair* @return*/public static String getPrivateKey(KeyPair keyPair) {PrivateKey privateKey = keyPair.getPrivate();byte[] bytes = base64Encode(privateKey.getEncoded());return new String(bytes, CHARSET);}/*** base64加密* @param bytes* @return*/public static byte[] base64Encode(byte[] bytes) {return Base64.getEncoder().encode(bytes);}/*** base64解密* @param bytes* @return*/public static byte[] base64Decode(byte[] bytes) {return Base64.getDecoder().decode(bytes);}/*** base64解密* @param src* @return*/public static byte[] base64Decode(String src) {return Base64.getDecoder().decode(src);}/*** 解密* @param key* @param data* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException* @throws NoSuchPaddingException* @throws InvalidKeyException* @throws IllegalBlockSizeException* @throws BadPaddingException*/public static String decrypt(String key, String data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {byte[] bytes = base64Decode(key);PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(bytes);KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, privateKey);return new String(cipher.doFinal(base64Decode(data.getBytes(CHARSET))), CHARSET);}
2.2前端使用公钥加密
安装这个依赖
npm install jsencrypt@3.2.0
import JSEncrypt from 'jsencrypt';// key为后端返回的公钥,this.form.password为明文密码
const jsEncrypt = new JSEncrypt();
jsEncrypt.setPublicKey(key);
// pwd为加密后的密码
var pwd = jsEncrypt.encrypt(this.form.password);
2.3后端使用私钥解密
从redis获取私钥,用私钥解密,得到明文后和数据库保存的密码比对,数据库的密码是以用户id为盐值对明文密码作md5加密,相同则放行,否则报错,注意登录成功的话需要把redis的密钥移除
ManagerController.java
@PostMapping("/login")public ResponseEntity<ApiResponse> login(@RequestParam("loginNo") String loginNo, @RequestParam("password") String password)throws Exception {UserDTOWithToken dto = managerService.login(loginNo, password);return ApiResponse.success(dto);}
ManagerServiceImpl.java
@Overridepublic UserDTOWithToken login(String loginNo, String password) throws Exception {QueryWrapper<Manager> wrapper = new QueryWrapper<>();wrapper.eq("loginNo", loginNo);Manager entity = this.getOne(wrapper);if (Objects.isNull(entity)) {throw new CodeException("用户不存在:loginNo=" + loginNo);}String redisKey = RedisKey.MANAGE_LOGIN_RSA_PRIVATEKEY + "$" + loginNo;// 从缓存获取私钥String privateKey = (String) redisService.get(redisKey);if (StrUtil.isEmpty(privateKey)) {throw new CodeException("私钥不存在");}// 用私钥解密String realPassword = RSAUtil.decrypt(privateKey, password);log.info("realPassword={}", realPassword);// 校验密码Digester digester = new Digester(DigestAlgorithm.MD5);digester.setSalt(entity.getId().getBytes(StandardCharsets.UTF_8));String encodePassword = digester.digestHex(realPassword);log.info("encodePassword={}", encodePassword);if (!encodePassword.equals(entity.getPassword())) {throw new CodeException("密码错误");}//从认证服务获取tokenResponseEntity<ApiResponse> responseEntity = authClient.token(AuthConst.PASSWORD_GRANT_TYPE, AuthConst.ADMIN_CLIENT_ID,AuthConst.ADMIN_CLIENT_SECRET, null, loginNo, password);ApiResponse response = responseEntity.getBody();TokenDTO tokenDTO = response.toObject(TokenDTO.class);if (Objects.isNull(tokenDTO)) {throw new CodeException("获取token失败:" + response.getMessage());}UserDTOWithToken dto = new UserDTOWithToken();dto.setUserId(entity.getId());dto.setToken(tokenDTO);entity.setLoginTime(LocalDateTime.now());Integer loginCount = entity.getLoginCount();entity.setLoginCount(Objects.isNull(loginCount) ? 1 : loginCount + 1);entity.setUpdateTime(LocalDateTime.now());this.updateById(entity);// 成功则清除缓存的私钥redisService.del(redisKey);return dto;}
3.总结
非对称加密还有其它算法,rsa是其中一种
后端存储私钥除了redis也可以用其它缓存工具如J2Cache
相关文章:
rsa加密登录解决方案
1.问题 账密登录方式中用户输入密码后,把账号、密码通过http传输到后端进行校验,然而密码属于敏感信息,不能以明文传输,否则容易被拦截窃取,因此需要考虑如何安全传输密码 2.解决方案 使用rsa加密方式,r…...
速盾:海外服务器用了cdn还是卡怎么办
海外服务器使用CDN卡顿问题的解决办法 在如今互联网高速发展的时代,海外服务器成为了许多企业和个人用户的首选,因为它能够提供更高的带宽和更稳定的网络连接。然而,尽管海外服务器在网络性能方面表现出色,但在使用过程中仍然可能…...
[python-opencv] PNG 裁切物体
拿到一组图PNG的图,边缘有点太宽了,需要裁切一下,为了这个需求,简单复习一下基本语法。 1. 读取PNG的4个通道 image cv.imread(image_path, cv.IMREAD_UNCHANGED) 附参数说明: IMREAD_UNCHANGED -1 返…...

机器学习——有监督学习和无监督学习
有监督学习 简单来说,就是人教会计算机学会做一件事。 给算法一个数据集,其中数据集中包含了正确答案,根据这个数据集,可以对额外的数据希望得到一个正确判断(详见下面的例子) 回归问题 例如现在有一个…...
MySQL单主模式部署组复制集群
前言 本篇文章介绍MySQL8.0.27版本的组复制详细搭建过程,教你如何快速搭建一个三节点的单主模式组复制集群。 实际上,MySQL组复制是MySQL的一个插件 group_replication.so,组中的每个成员都需要配置并安装该插件,配置和安装过程…...

【大厂AI课学习笔记】【1.5 AI技术领域】(10)对话系统
对话系统,Dialogue System,也称为会话代理。是一种模拟人类与人交谈的计算机系统,旨在可以与人类形成连贯通顺的对话,通信方式主要有语音/文本/图片,当然也可以手势/触觉等其他方式 一般我们将对话系统,分…...

【ARM 嵌入式 编译系列 2.7 -- GCC 编译优化参数详细介绍】
请阅读【嵌入式开发学习必备专栏 】 文章目录 GCC 编译优化概述常用优化等级-O1 打开的优化选项-O2 打开的优化选项-O3 打开的优化选项-Os 打开的优化选项优化技术使用优化选项的注意事项GCC 编译优化概述 GCC(GNU Compiler Collection)包含了用于C、C++、Objective-C、Fort…...

《剑指 Offer》专项突破版 - 面试题 38、39 和 40 : 通过三道面试题详解单调栈(C++ 实现)
目录 面试题 38 : 每日温度 面试题 39 : 直方图最大矩形面积 方法一、暴力求解 方法二、递归求解 方法三、单调栈法 面试题 40 : 矩阵中的最大矩形 面试题 38 : 每日温度 题目: 输入一个数组,它的每个数字是某天的温度。请计算每天需要等几天才会…...
动态规划C语言
#include <stdio.h> #include <stdlib.h> //0-1背包问题是一种经典的组合优化问题, //问题描述为:有一个给定容量的背包和一组具有不同价值和重量的物品,如何选择物品放入背包中,以使得背包中物品的总价值最大化&…...

基于微信小程序的校园二手交易平台
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...

K8S系列文章之 [使用 Alpine 搭建 k3s]
官方文档:K3s - 轻量级 Kubernetes | K3s 官方描述,可运行在 systemd 或者 openrc 环境上,那就往精简方向走,使用 alpine 做系统。与 RHEL、Debian 的区别,主要在防火墙侧;其他基础配置需求类似࿰…...

计算机视觉 | OpenCV 实现手势虚拟控制亮度和音量
Hi,大家好,我是半亩花海。在当今科技飞速发展的时代,我们身边充斥着各种智能设备,然而,如何更便捷地与这些设备进行交互却是一个不断被探索的课题。本文将主要介绍一个基于 OpenCV 的手势识别项目,通过手势…...

python28-Python的运算符之三目运算符
Python可通过if语句来实现三目运算符的功能,因此可以近似地把这种if语句当成三目运算符。作为三目运算符的f语句的语法格式如下 True_statements if expression else False_statements 三目运算符的规则是:先对逻辑表达式expression求值,如果逻辑表达式…...

高德 API 10009
问题 笔者使用高德地图所提供的API接口,访问接口报错 {"info":"USERKEY_PLAT_NOMATCH","infocode":"10009","status":"0","sec_code_debug":"d41d8cd98f00b204e9800998ecf8427e"…...

Go 语言中如何大小端字节序?int 转 byte 是如何进行的?
嗨,大家好!我是波罗学。 本文是系列文章 Go 技巧第十五篇,系列文章查看:Go 语言技巧。 我们先看这样一个问题:“Go 语言中,将 byte 转换为 int 时是否涉及字节序(endianness)&#x…...

论文阅读——MP-Former
MP-Former: Mask-Piloted Transformer for Image Segmentation https://arxiv.org/abs/2303.07336 mask2former问题是:相邻层得到的掩码不连续,差别很大 denoising training非常有效地稳定训练时期之间的二分匹配。去噪训练的关键思想是将带噪声的GT坐标…...

JPEG图像的压缩标准(1)
分3个博客详细介绍JPEG图像的压缩标准,包含压缩和解压缩流程,熵编码过程和文件存储格式。 一、JPEG压缩标准概述 JPEG压缩标准由国际标准化组织 (International Organization for Standardization, ISO) 制订,用于静态图像压缩。JPEG标准包…...
数解 transformer 之 self attention transformer 公式整理
句子长度为n;比如2048,或1024,即,一句话最多可以是1024个单词。 1, 位置编码 可知,E是由n个列向量组成的矩阵,每个列向量表示该列号的位置编码向量。 2, 输入向量 加入本句话第一个单词的词嵌入向量是, 第…...
ubuntu22.04@laptop OpenCV Get Started
ubuntu22.04laptop OpenCV Get Started 1. 源由2. 步骤3. 预期&展望4. 参考资料 1. 源由 OpenCV在学校的时候接触过,不过当时专注在物理、研究方面,没有好好的学习下。 这次借后续视频分析刚性需求,对OpenCV做个入门的学习和研读&#…...

【Java】苍穹外卖 Day01
苍穹外卖-day01 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示: 管理端-外卖商家使用用户端-点餐用户使用当我们完成该项目的学习,可以培养以下能力: 1. 软件开发整体介绍 作为一名软件开…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...