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

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

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

相关文章

795. 前缀和

Problem: 795. 前缀和 文章目录 思路解题方法复杂度Code 思路 这是一个前缀和的问题。前缀和是一种预处理方法&#xff0c;通过预处理出一个数组的前缀和&#xff0c;可以在O(1)的时间复杂度内求出任意一个区间的和。 在这个问题中&#xff0c;我们需要求出数组中某个区间的和。…

华为机考入门python3--(10)牛客10-字符个数统计

分类&#xff1a;字符 知识点&#xff1a; 字符的ASCII码 ord(char) 题目来自【牛客】 def count_unique_chars(s): # 创建一个空集合来保存不同的字符 unique_chars set() # 遍历字符串中的每个字符 for char in s: # 将字符转换为 ASCII 码并检查是否在范围内 #…

KEIL-MDK的时间戳之time.h 结合gd32f1的RTC应用

KEIL-MDK的时间戳之time.h 的应用 1 时间戳介绍 现在物联网产品的在进行通讯的时候&#xff0c;需要加入时间戳的这个信息参数&#xff0c;方便服务器和产品之间交换时间信息。 时间戳是计算机系统中用来表示日期和时间的一种方式&#xff0c;通常是一个数字或者一串字符&am…

记一次deepin开机突然进不了可视化界面,只能进命令行

看见开机报错failed to start file system on /dev/disk/by-uid/246… 文件系统出现损坏 然后只能在控制台输入密码登录root账号 直接使用这个命令尝试&#xff0c;然后reboot命令重启 fsck /dev/disk/by-uid/246...重启之后又可以进图形化界面了

[优雅的面试] 进程 线程 协程分的清

面试官大佬&#xff1a;小伙子&#xff0c;咱今儿个先聊聊进程线程这块的知识哈&#xff0c;就先说说进程吧。 我&#xff1a;存储在硬盘中的代码是静态文件&#xff0c;运行中的程序被称为进程。进程之间数据是相互隔离的。 一般说来&#xff0c;一个进程并不是自始至终连续不…

Vits2.3-Extra-v2:中文特化,如何训练及推理(新手教程)

环境&#xff1a; Vits2.3-Extra-v2:中文特化修复版 auto_DataLabeling 干声10分钟左右.wav 问题描述&#xff1a; Vits2.3-Extra-v2:中文特化&#xff0c;如何训练及推理&#xff08;新手教程&#xff09; 解决方案&#xff1a; 一、准备数据集 切分音频 本次音频数据…

管理就是闭环

管理是什么&#xff1f;这个问题没有一个统一的答案。本文提供一个管中窥豹的答案&#xff1a;管理就是闭环。 作为基层管理者&#xff0c;日常管理事务&#xff0c;一个是目标闭环&#xff0c;一个是执行闭环。这分别对应敏捷PO和Scrum Master的职责。PO的职责是确保目标闭环&…

vue3学习——router-view 过渡动画

虽然vue3说建vue页面不用包裹一个根节点&#xff0c;但是transition不能没有唯一的标签 所以还是得包一层~ o(&#xffe3;▽&#xffe3;)o <el-main><router-view v-slot"{ Component, route }"><transition name"MainFade" mode"o…

猫头虎分享已解决Bug ‍ || TypeError: props is not a function (React)

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

linux赋予普通用户权限

当我们在Linux系统中操作文件或目录时&#xff0c;有时候会遇到权限的问题。为了正确管理文件和目录的所有权&#xff0c;我们可以使用sudo chown命令。在本文中&#xff0c;我将介绍这个命令以及它的一个示例用法。 什么是sudo chown命令&#xff1f; sudo chown是Linux系统…

【Vue项目】filters过滤器

在vue项目中需要对一些数据进行格式化&#xff0c;我们可以通过Vue.filter进行过滤 Vue.filter 过滤器在 Vue 实例中使用 Vue.filter 方法进行注册&#xff0c;注册成功就可以在任何 Vue模板中使用这些过滤器来对数据进行处理和格式化。 Vue项目中&#xff0c;过滤器可以在全…

05.坐标系

1. 坐标系原点 坐标系原点就是屏幕/窗口的左上角&#xff0c;X向右增长&#xff0c;Y向下增长。 2.设置控件位置 设置控件位置&#xff0c;就相当于是需要指定控件的坐标&#xff0c;对于该控件来说&#xff0c;其坐标原点是其父窗口/父控件的左上角。 设置方法就是通过控件的…

创新S3存储桶检索:Langchain社区S3加载器搭载OpenAI API

在瞬息万变的数据存储和处理领域&#xff0c;将高效的云存储解决方案与先进的 AI 功能相结合&#xff0c;为处理大量数据提供了一种变革性的方法。本文演示了使用 MinIO、Langchain 和 OpenAI 的 GPT-3.5 模型的实际实现&#xff0c;重点总结了存储在 MinIO 存储桶中的文档。 …

leetcode:62.不同路径

动态规划 注意&#xff1a;用深搜或者广搜会超时 dp含义dp[i,j]&#xff1a;从&#xff08;0&#xff0c;0&#xff09;到&#xff08;i&#xff0c;j&#xff09;的路径个数 递推公式&#xff1a;dp[i][j] dp[i-1][j] dp[i][j-1] 初始化&#xff1a;for(i 0,i<m,i) …

[职场] 面试被问优点的回答参考 #知识分享#其他#学习方法

面试被问优点的回答参考 当面试官问你最大的优点是什么&#xff1f;回答1&#xff1a; 我擅长合理地安排时间&#xff0c; 作为助理&#xff0c; 我的杂事很多&#xff0c; 总是觉得手边有做不完的事情&#xff0c; 所以我特别注意时间管理&#xff0c; 这样才能高效地工作&am…

继承

1.继承的作用 有些类与类之间存在特殊关系&#xff0c;下级别的成员除了拥有上一级别的共性&#xff0c;还有自己的特性。 这个时候我们就可以考虑利用继承技术&#xff0c;减少重复代码。 总结&#xff1a; 继承的好处&#xff1a;可以减少重复的代码 class A : public B;…

猫头虎分享已解决Bug || KeyError: ‘The truth value of a Series is ambiguous‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

并发事务带来的问题及解决方法

引言 在数据库系统中&#xff0c;事务是指一组操作被视为一个逻辑单元&#xff0c;要么全部执行成功&#xff0c;要么全部不执行&#xff0c;保证数据库的一致性和完整性。而并发事务则是指多个事务同时执行的情况。虽然并发事务能够提高系统的性能和吞吐量&#xff0c;但也会…

EasyExcel动态列导出

测试代码地址&#xff1a;https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn 官方文档&#xff1a;https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write 一、实现方式 1、根据需要导出的列…

假期刷题打卡--Day26

1、MT1212乘法表 请编写一个简单程序&#xff0c;输出九九乘法表。输入n&#xff0c;就输出乘法表到n的地方。 格式 输入格式&#xff1a; 输入整型 输出格式&#xff1a; 输出整型。形式如&#xff1a;1*11 样例 1 输入&#xff1a; 5输出&#xff1a; 1*11 2*12 …