ruoyi-vue框架密码加密传输

news2024/10/26 3:26:46

先看一下改造后的样子,输入的密码不会再以明文展示。

下面我主要把前后端改造的代码贴出来。

1.后端代码

RsaUtils类

在com.ruoyi.common.utils包下新建RsaUtils类,RsaUtils添加了@Component注解 generateKeyPair()构建密钥对添加了@Bean注解 在项目启动时通过@Bean的方式将普通类事例化到spring容器中 所以在系统启动后 每次调用接口会是一样的公私秘钥 每次重启后公私钥不同

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@Component
public class RsaUtils {
    // Rsa 私钥 也可固定秘钥对 若依原写法(不安全)
    public static String privateKeys = "";
    private static String publicKeyStr = "";
    private static String privateKeyStr = "";
    private static final RsaKeyPair rsaKeyPair = new RsaKeyPair();
    private static final Logger logger = LoggerFactory.getLogger(RsaUtils.class);

    /**
     * 私钥解密
     *
     * @param text 待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String text) throws Exception {
//        logger.info(rsaKeyPair.getPrivateKey());
        return decryptByPrivateKey(rsaKeyPair.getPrivateKey(), text);
    }

    /**
     * 公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            待解密的信息
     * @return 解密后的文本
     */
    public static String decryptByPublicKey(String publicKeyString, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 私钥加密
     *
     * @param privateKeyString 私钥
     * @param text             待加密的信息
     * @return 加密后的文本
     */
    public static String encryptByPrivateKey(String privateKeyString, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 私钥解密
     *
     * @param privateKeyString 私钥
     * @param text             待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String privateKeyString, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 公钥加密
     *
     * @param publicKeyString 公钥
     * @param text            待加密的文本
     * @return 加密后的文本
     */
    public static String encryptByPublicKey(String publicKeyString, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 构建RSA密钥对
     *
     * @return 生成后的公私钥信息
     */
    @Bean
    public void generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
        rsaKeyPair.setPrivateKey(privateKeyString);
        rsaKeyPair.setPublicKey(publicKeyString);
        publicKeyStr = publicKeyString;
        privateKeyStr = privateKeyString;
    }


    public static String getPublicKey() {
        return publicKeyStr;
    }

    public static String getPrivateKey() {
        return privateKeyStr;
    }

    public static RsaKeyPair rsaKeyPair() {
        return rsaKeyPair;
    }

    /**
     * RSA密钥对对象
     */
    public static class RsaKeyPair {
        private String publicKey;
        private String privateKey;

        public void setPublicKey(String publicKey) {
            this.publicKey = publicKey;
        }

        public void setPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }

        public RsaKeyPair() {

        }

        public RsaKeyPair(String publicKey, String privateKey) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }

        public String getPublicKey() {
            return publicKey;
        }

        public String getPrivateKey() {
            return privateKey;
        }
    }
}

在SysLoginController添加获取公钥接口

/**
     * 获取公钥 前端用来密码加密
     *
     * @return
     */
    @GetMapping("/publicKey")
    public RsaUtils.RsaKeyPair publicKey() {
        return RsaUtils.rsaKeyPair();
    }

修改SysLoginService类中的login方法

/**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid)
    {
        // 验证码校验
        validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try
        {
            //RsaUtils.decryptByPrivateKey(password)解密密码
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,  RsaUtils.decryptByPrivateKey(password));
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

修改重置密码

/**
     * 重置密码
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updatePwd")
    public AjaxResult updatePwd(String oldPassword, String newPassword) throws Exception {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        String userName = loginUser.getUsername();
        //加密后的
        String password = loginUser.getPassword();
        //解密
        oldPassword = RsaUtils.decryptByPrivateKey(oldPassword);
        newPassword = RsaUtils.decryptByPrivateKey(newPassword);
        //拿原密码和加密后的解密
        if (!SecurityUtils.matchesPassword(oldPassword, password)) {
            return AjaxResult.error("修改密码失败,旧密码错误");
        }
        if (SecurityUtils.matchesPassword(newPassword, password)) {
            return AjaxResult.error("新密码不能与旧密码相同");
        }
        if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) {
            // 更新缓存用户密码
            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
            tokenService.setLoginUser(loginUser);
            return AjaxResult.success();
        }
        return AjaxResult.error("修改密码异常,请联系管理员");
    }

在SecurityConfig中添加获取公钥接口

因为密码校验时会校验长度,加密后的密码会超出框架内原来设定的长度,修改UserConstants类中的PASSWORD_MAX_LENGTH

2.前端代码变动

jsencrypt.js

import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'

// 密钥对生成 http://web.chacuo.net/netrsakeypair

//const publicKey = ''

//const privateKey = ''

// 加密
export function encrypt(txt, publicKey) {
  const encryptor = new JSEncrypt()
  encryptor.setPublicKey(publicKey) // 设置公钥
  return encryptor.encrypt(txt) // 对数据
}

// 解密(暂无使用)
export function decrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPrivateKey(privateKey) // 设置私钥
  return encryptor.decrypt(txt) // 对数据进行解密
}


login.js添加获取公钥路由

// 获取key
export function getPublicKey() {
  return request({
    url: '/publicKey',
    method: 'get',
  })
}

user.js修改登入方法,方便copy我直接全放进来了

import { login, logout, getInfo,getPublicKey } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { encrypt, decrypt } from '@/utils/jsencrypt'

const user = {
  state: {
    token: getToken(),
    id: '',
    name: '',
    avatar: '',
    roles: [],
    permissions: []
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_ID: (state, id) => {
      state.id = id
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    }
  },

  actions: {
    getPublicKey() {
      return new Promise((resolve, reject) => {
        getPublicKey()
          .then(res => {
            resolve(res)
          })
          .catch(error => {
            reject(error)
          })
      })
    },
    // 登录
    Login({ commit, dispatch }, userInfo) {
      return new Promise((resolve, reject) => {
        dispatch('getPublicKey').then(res => {
          let publicKey = res.publicKey
          const username = userInfo.username.trim()
          //调用加密方法(传密码和公钥)
          const password = encrypt(userInfo.password, publicKey)
          const code = userInfo.code
          const uuid = userInfo.uuid
          login(username, password, code, uuid)
            .then(res => {
              setToken(res.token)
              commit('SET_TOKEN', res.token)
              resolve()
            })
            .catch(error => {
              reject(error)
            })
        })
      })
    },

    // 获取用户信息
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          const user = res.user
          const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
          if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
            commit('SET_ROLES', res.roles)
            commit('SET_PERMISSIONS', res.permissions)
          } else {
            commit('SET_ROLES', ['ROLE_DEFAULT'])
          }
          commit('SET_ID', user.userId)
          commit('SET_NAME', user.userName)
          commit('SET_AVATAR', avatar)
          resolve(res)
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 退出系统
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}

export default user

resetPwd.vue

methods: {
    getPublicKey() {
      return new Promise((resolve, reject) => {
        getPublicKey()
          .then(res => {
            resolve(res)
          })
          .catch(error => {
            reject(error)
          })
      })
    },
    submit() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          this.getPublicKey().then(res=>{
            let publicKey = res.publicKey
            console.log("res.publicKey",res.publicKey)
            const oldPassword = encrypt(this.user.oldPassword, publicKey)
            const newPassword = encrypt(this.user.newPassword, publicKey)
            updateUserPwd(oldPassword, newPassword).then(
              response => {
                this.msgSuccess("修改成功");
              }
            );
          })

        }
      });
    },
    close() {
      this.$store.dispatch("tagsView/delView", this.$route);
      this.$router.push({ path: "/index" });
    }
  }

这样就完成改造了。

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

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

相关文章

AI助力剧本创作:如何5分钟内构思出热门短剧大纲

人工智能重塑短剧行业:从剧本创作到市场推广 在当今短剧行业的飞速发展中,剧本创作的质量及其更新的速度已然成为短剧能否转化为热门作品的关键性因素。然而,随着短剧创作成本的日益攀升,一个卓越的剧本无论在创作时间上还是在构思…

java中的多线程通信问题介绍

在多线程编程中,通信是线程间协调和同步的重要手段。由于线程是独立执行的,它们需要一种机制来交换信息和协调它们的行为。Java 提供了多种方式来实现线程间的通信,包括共享内存、消息传递、条件变量和共享对象等。 1. 共享内存 在 Java 中&a…

vue2的element UI 表格单选

代码 this.$refs.multipleTable.toggleRowSelection(selection.shift(), false);multipleTable 是定义的表格的ref

Kubernetes(K8S之存储)

configmap configMap描述信息 configMap功能在Kubernetes1.2版本中引入,许多应用程序会从配置文件,命令行参数或环境变量中读取配置信息。ConfigMap API给我们提供了向容器中注入配置信息的机制。ConfigMap可以被用来保存单个属性。 也可以用来保存整…

二叉树前序遍历函数 代码图解(先序遍历 深度优先遍历)

void PreOrder(BiTree p)//只是遍历 即只是读,不会改变树根 {//这个p的类型是 树的结构体 不是之前的p指针if(p!NULL){printf("%c", p->c);PreOrder(p->lchild);//函数嵌套 打印左子树PreOrder(p->rchild);//函数嵌套 打印右子树} } 同理可证 中…

如何在Win系统本地部署Jupyter Notbook交互笔记并结合内网穿透实现公网远程使用

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中,使用最多的无疑就是各种函数、图表、…

windows部署ruoyi-vue-pro

前提 安装java 安装maven 安装redis mysql 源代码下载 后端 ruoyi-vue-pro 前端 yudao-ui-admin-vue3 后端项目 配置maven 导入数据 CREATE DATABASE ruoyi_vue_pro;修改mysql连接配置 修改redis 打包项目 mvn clean install package -Dmaven.test.skiptrue启动YudaoSe…

Linux CentOS系统安装Spug并结合内网穿透实现远程访问本地运维平台

目录 前言 1. Docker安装Spug 2 . 本地访问测试 3. Linux 安装cpolar 4. 配置Spug公网访问地址 5. 公网远程访问Spug管理界面 6. 固定Spug公网地址 结语 作者简介: 懒大王敲代码,计算机专业应届生 今天给大家聊聊Linux CentOS系统安装Spug并结合…

19-Java中介者模式 ( Mediator Pattern )

Java中介者模式 摘要实现范例 中介者模式(Mediator Pattern)提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护中介者模式是用来降低多个对象和类之间的通信复杂性中介者模式属于行为型模式…

快递包装展|2024上海国际电商物流包装产业展览会

2024中国(上海)国际电商物流包装产业展览会 2024 China (Shanghai) international e-commerce logistics packaging industry exhibition 时 间:2024年7月24日 —7月26日 地 点:国家会展中心(上海市青浦区崧泽大道333号&#xff…

Vue3.0 vue.js.devtools无法显示Pinia调试工具

之前的配置方式: app.use(createPinia()) app.mount(#app) 更新配置方式: app.use(createPinia()).mount("#app") 设置之后即可显示调试工具

Python学习笔记-Flask实现简单的抽奖程序

1.导入flask包和randint包 from flask import Flask,render_template from random import randint 2.初始化 Flask 应用: app Flask(__name__) 3. 定义英雄列表 hero [黑暗之女,狂战士,正义巨像,卡牌大师,德邦总管,无畏战车,诡术妖姬,猩红收割者,远古恐惧,正义天使,无极剑…

kasan排查kernel内存越界示例(linux5.18.11)

参考资料: 1,内核源码目录中的Documentation\dev-tools\kasan.rst 2,KASAN - Kernel Address Sanitizer | Naveen Naidu (naveenaidu.dev) 一、kasan实现原理 KASAN(Kernel Address SANitizer)是一个动态内存非法访…

Leetcoder Day39| 动态规划part06 完全背包问题

完全背包理论 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。 示例: 背包最大…

CentOS下安装RabbitMQ

准备工作,更新yum源 正式环境慎用 yum update -y # 进入目录 cd /etc/yum.repos.d/ # 创建目录 mkdir backup # 默认源配备份 mv C* backup/ # 下载阿里云yum源 wget -O /etc/yum.repos.d/CenOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo # 清除旧…

【BUG修复日志】Anaconda + VSCode 编码错误

【BUG修复日志】Anaconda VSCode 编码错误 平台: Windows11家庭版 (v22621.3155) 软件: Visual Studio Code (v1.87.0) 插件: Python (v2024.2.1) 版本: Conda (v24.1.2)问题描述 VSCode 在安装 Python 插件的情况下自动提示配置 Conda 环境,但是在自动配置完成后…

P9889 [ICPC2018 Qingdao R] Plants vs. Zombies 题解 二分+贪心

[ICPC2018 Qingdao R] Plants vs. Zombies 传送门 题面翻译 给定 n n n 个植物和 m m m 的步数限制,每个植物在位置 1 … n 1\dots n 1…n 上。你初始时在位置 0 0 0,每次可以移动到相邻的位置上。 每次设你走完一步后到达的位置是 i i i&#…

LeetCode每日一题之 快乐数

目录 题目介绍: 算法原理: 鸽巢原理: 如何找到环里元素: 代码实现: 题目介绍: 题目链接:. - 力扣(LeetCode) 算法原理: 我先简单举两个例子&#xff…

最新的前端开发技术(2024年)

关于作者: 还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas&#xff0…

如何将虚拟机设置成固定IP

问题描述: 在VMware虚拟机上部署的项目ip地址和数据库ip地址发生变动,导致mysql,nginx,redis等无法访问,要改配置又特别麻烦,而且下次可能还会变动。 解决方法: 将虚拟机ip地址配置成固定ip 关闭虚拟机,找…