首页 > 其他分享 >请求参数加密与返回结果加密

请求参数加密与返回结果加密

时间:2023-07-09 18:32:16浏览次数:39  
标签:encrypt 请求 rsa 参数 RAS import 加密 密钥

在开发接口的过程中,难免会遇到隐私数据的传输,如账号密码、支付密码等。以明文的方式传输这些信息,会有泄露给别人的风险,这是就需要加密传输

我们要首先选择加密算法

加密算法分为三种:

  1. 对称加密
    对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。常见的算法主要有 AESDES
  2. 非对称加密
    非对称加密算法,又称为公开密钥加密算法。它需要两个密钥,一个称为公开密钥 (public key),即公钥;另一个称为私有密钥 (private key),即私钥。 他俩是配对生成的,就像钥匙和锁的关系。因为加密和解密使用的是两个不同的密钥,所以这种算法称为非对称加密算法。其优点是算法强度复杂、安全性高;缺点是加解密速度没有对称加密算法快。常见的算法主要有:RSA、DSA
  3. 摘要加密
    摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是:无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度的,这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障。
    但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。常见的算法主要有MD5、SHA-1、SHA-256、HMAC

我们这里使用 非对称加密中的 RSA加密

请求参数加密实现的方法

生成加密的RAS公钥与密钥

见我这篇文章 RAS公钥与密钥生成

前端加密实现

引入 Node.js RSA 库

yarn add node-rsa

设置常量

/**
 * 证书 密钥 加密使用
 * @type {string}
 */
export const RAS_PRIVATE =
  `-----BEGIN PRIVATE KEY-----   ...  -----END PRIVATE KEY-----`

/**
 * 证书 公钥 加密使用
 * @type {string}
 */
export const RAS_PUBLIC =
  `-----BEGIN PUBLIC KEY-----  ...  -----END PUBLIC KEY-----`

加密工具类

import NodeRSA from "node-rsa";
import {RAS_PRIVATE, RAS_PUBLIC} from "@/ras-key.js"


const crypto = {}

/**
 * 加密方法
 * @param cipher
 * @returns {*}
 */
crypto.encrypt = function (cipher = {}) {
  const rsa = new NodeRSA(RAS_PUBLIC)
  rsa.setOptions({encryptionScheme: 'pkcs1'})
  let encrypt = rsa.encrypt(JSON.stringify(cipher), 'base64');
  return {
    encrypt
  }
}


/**
 * 解密方法 
 * @returns {string}
 * @param decryptData
 */
crypto.decryption = function (decryptData  = '') {
  let rsa = new NodeRSA(RAS_PRIVATE)
  rsa.setOptions({ encryptionScheme: 'pkcs1' });
  let data = rsa.decrypt(decryptData, 'utf8');

  return JSON.parse(data);
}

export default crypto

接口调用

SYS_USER_LOGIN(data = {}) {
    // 接口请求
    return request({
      url: '/auth/login',
      method: 'post',
      data: util.crypto.encrypt(data)
    })
  }

这样请求参数就加密了

请求参数加密与返回结果加密_ci

后端解密实现

我这里使用Hutool工具类库

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-crypto</artifactId>
    <version>5.7.12</version>
</dependency>

解密实现

// RasConfig.RAS_PRIVATE 为密钥 
// JAVA里的密钥不能带类似 -----BEGIN PRIVATE KEY-----  的头和尾
// encrypt 为密文
RSA rsa = new RSA(RasConfig.RAS_PRIVATE, null);
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
String decryptSt = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);

返回结果加密实现

生成加密的RAS公钥与密钥(同上)

后端加密实现

加密的实现

public EncryptParam(Object data) {
    String param = JSONUtil.getBeanToJson(data);
    RSA rsa = new RSA(null,RasConfig.RAS_PUBLIC);
    byte[] encryptByte = rsa.encrypt(param, KeyType.PublicKey);
    this.encrypt =  Base64.encodeBase64String(encryptByte);
}

前台解密的实现

import NodeRSA from "node-rsa";
import {RAS_PRIVATE, RAS_PUBLIC} from "@/ras-key.js"


const crypto = {}

/**
 * 加密方法
 * @param cipher
 * @returns {*}
 */
crypto.encrypt = function (cipher = {}) {
  const rsa = new NodeRSA(RAS_PUBLIC)
  rsa.setOptions({encryptionScheme: 'pkcs1'})
  let encrypt = rsa.encrypt(JSON.stringify(cipher), 'base64');
  return {
    encrypt
  }
}


/**
 * 解密方法 
 * @returns {string}
 * @param decryptData
 */
crypto.decryption = function (decryptData  = '') {
  let rsa = new NodeRSA(RAS_PRIVATE)
  rsa.setOptions({ encryptionScheme: 'pkcs1' });
  let data = rsa.decrypt(decryptData, 'utf8');

  return JSON.parse(data);
}

export default crypto

这样结果就加密了

请求参数加密与返回结果加密_ci_02


下面的代码记录我自己的实现

我的思路是所有的加密参数都使用加密的实体类接受,并解析

接口返回的时候有设置一个加密参数,前端请求拦截的时候解密

package org.example.framework.config;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * 读取RAS 公钥密钥
 * Created by myy on 2021/9/16.
 */
@Configuration
@Slf4j
public class RasConfig {

    /**
     * 密钥钥
     */
    public static String RAS_PRIVATE;
    /**
     * 公钥
     */
    public static String RAS_PUBLIC;

    @Bean
    public void getPublicKey() {
        log.info("开始 读取RAS加密公钥");
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("ras/rsa_public_key.pem");
        try {
            assert inputStream != null;
            RAS_PUBLIC = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            if (ObjectUtils.isEmpty(RAS_PUBLIC)) {
                log.info("读取RAS加密密钥 未空");
            } else {
                log.info("读取RAS加密密钥 成功");
            }
        } catch (IOException e) {
            log.error("读取RAS加密公钥失败");
            e.printStackTrace();
        } finally {
            try {
                assert inputStream != null;
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Bean
    public void getPrivateKey() {
        log.info("开始 读取RAS加密密钥");
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("ras/rsa_private_key.pem");
        try {
            assert inputStream != null;
            RAS_PRIVATE = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            if (ObjectUtils.isEmpty(RAS_PRIVATE)) {
                log.info("读取RAS加密密钥 为空");
            } else {
                log.info("读取RAS加密密钥 成功");
            }
        } catch (IOException e) {
            log.error("读取RAS加密密钥失败");
            e.printStackTrace();
        } finally {
            try {
                assert inputStream != null;
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

解密的实体

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.example.common.util.JSONUtil;
import org.example.framework.config.RasConfig;
import org.springframework.util.ObjectUtils;
import org.apache.commons.codec.binary.Base64;


/**
 * 加密参数
 * Created by myy on 2021/9/14.
 */
@Getter
@Setter
@Slf4j
public class EncryptParam {

    /**
     * 加密参数
     */
    private String encrypt;


    public EncryptParam() {
    }


    /**
     * 加密参数
     * @param data
     */
    public EncryptParam(Object data) {
        String param = JSONUtil.getBeanToJson(data);
        RSA rsa = new RSA(null,RasConfig.RAS_PUBLIC);
        byte[] encryptByte = rsa.encrypt(param, KeyType.PublicKey);
        this.encrypt =  Base64.encodeBase64String(encryptByte);
    }

    /**
     * 得到解密的内容
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getParam(Class<T> clazz) {
        try {
            log.info("密文: {}", encrypt);
            // 解密
            RSA rsa = new RSA(RasConfig.RAS_PRIVATE, null);
            byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
            String decryptSt = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
            if (ObjectUtils.isEmpty(decryptSt)) {
                throw new RuntimeException("解密后字符串为空");
            }
            log.info("解密后内容: {}", decryptSt);

            // 将字符串转换成实体
            T t = JSONUtil.getJsonToBean(decryptSt, clazz);
            if (ObjectUtils.isEmpty(t)) {
                throw new RuntimeException("JSON数据转换成对象失败");
            }
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解密失败");
            throw new RuntimeException("解密失败");
        }
    }

}

接口参数解密

@RequestMapping("/login")
public ResponseData login(@RequestBody EncryptParam param) {
    LoginDTO loginDTO = param.getParam(LoginDTO.class);
    return new SuccessResponseData(authService.login(loginDTO));
}

修改 Response

package org.example.framework.web.response;

import org.example.framework.web.controller.EncryptParam;

/**
 * Created by myy on 2021/9/6.
 */
public class SuccessResponseData extends ResponseData {

    public SuccessResponseData() {
        super(Boolean.TRUE, "200", "请求成功", (Object) null, traceID());
    }

    public SuccessResponseData(Object object) {
        super(Boolean.TRUE, "200", "请求成功", object, traceID());
    }

    public SuccessResponseData(String code, String message, Object object) {
        super(Boolean.TRUE, code, message, object, traceID());
    }

    /**
     * 参数加密
     */
    public SuccessResponseData encrypt() {
        setEncrypt(true);
        setData(new EncryptParam(getData()));
        return this;
    }


}

接口加密

@RequestMapping("/treeMenu")
public ResponseData treeMenu() {
    return new SuccessResponseData(menuService.treeMenu()).encrypt();
}

前端 工具类

import NodeRSA from "node-rsa";
import {RAS_PRIVATE, RAS_PUBLIC} from "@/ras-key.js"


const crypto = {}

/**
 * 加密方法
 * @param cipher
 * @returns {*}
 */
crypto.encrypt = function (cipher = {}) {
  const rsa = new NodeRSA(RAS_PUBLIC)
  rsa.setOptions({encryptionScheme: 'pkcs1'})
  let encrypt = rsa.encrypt(JSON.stringify(cipher), 'base64');
  return {
    encrypt
  }
}


/**
 * 解密方法
 * @returns {string}
 * @param decryptData
 */
crypto.decryption = function (decryptData  = '') {
  let rsa = new NodeRSA(RAS_PRIVATE)
  rsa.setOptions({ encryptionScheme: 'pkcs1' });
  let data = rsa.decrypt(decryptData, 'utf8');

  return JSON.parse(data);
}

export default crypto

接口加密

SYS_USER_LOGIN(data = {}) {
  // 接口请求
  return request({
    url: '/auth/login',
    method: 'post',
    data: util.crypto.encrypt(data)
  })
}

响应拦截解密

// 响应拦截
service.interceptors.response.use(
  response => {
    // dataAxios 是 axios 返回数据中的 data
    const dataAxios = response.data
    const {success, data, encrypt} = dataAxios
    // 根据 success 进行判断
    if (success) {
      // 判断参数是否加密
      if (encrypt){
        let decryption = util.crypto.decryption(data.encrypt)
        console.log("解密 {}",decryption)
        return decryption
      }else {
        return data
      }
    } else {
      errorCreate(`${dataAxios.message}`)
    }
  },
  error => {
   ...
  }
)

标签:encrypt,请求,rsa,参数,RAS,import,加密,密钥
From: https://blog.51cto.com/u_16007752/6668698

相关文章

  • 详解Django请求与响应:深入理解Web Http交互的核心机制
    本文深入探讨了Django中的请求与响应处理,从Django请求和响应的基础知识、生命周期,到HttpRequest和HttpResponse对象的详细介绍。同时,讨论了Django的视图和请求、响应处理,以及安全性和异步处理的考虑。最后,对比了Django与Flask、FastAPI等框架在请求响应处理上的异同......
  • 如何自动(定时/间隔/重复)执行 同步文件、备份打包加密压缩文件
    参考下列两个教程结合使用即可:快捷自由定时重启、注销、关机如何从多个文件夹内转移全部文件(忽略文件夹的结构)(进行复制)(再打包)就是先设定好勾选对'来源路径’Zip打包,并且勾选备份模式备份模式下,就会先对要复制的文件(夹)先打包,然后可以再对包重命名,再复制到指定的位置中去保......
  • MD5加密介绍
      MD5的全称是Message-DigestAlgorithm5,Message-Digest泛指字节串(Message)的Hash变换,就是把一个任意长度的字节串变换成一定长的大整数。MD5将任意长度的"字节串"变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法,换句话说就是,即使你看到源程序和算法描述,也无法......
  • 06、etcd 写请求执行流程
    本篇内容主要来源于自己学习的视频,如有侵权,请联系删除,谢谢。上一节我们学习了etcd读请求执行流程,这一节,我们来学习etcd写请求执行流程。1、etcd写请求概览etcd一个写请求执行流程又是怎样的呢?etcdctl put hello world ‐‐endpoints 192.168.65.210:2379执行流程......
  • git --date 常用参数 【汇总】
    详细使用说明:https://www.cnblogs.com/wutou/p/17493337.html其他的格式化占位符如下:--date=format:relative:'5secondsago'--date=format:relative:'5minutesago'--date=format:relative:'5hoursago'--date=format:relative:'5daysa......
  • git log --pretty=format 常用选项参数 【汇总】
    --pretty=format常用选项[注]选项说明速记%H提交的完整哈希值commithash%h提交的简写哈希值Abbreviatedcommithash%T树的完整哈希值treehash%t树的简写哈希值Abbreviatedtreehash%P父提交的完整哈希值Parenthashes%p父提交的简写......
  • 函数的变量作用域、返回值、参数
    函数的变量作用域以及访问、修改全局变量1'''2函数往往涉及2类变量:31.全部变量,直接访问,修改需要使用global关键字42.局部变量,函数内部定义的变量5'''67#1.全局变量8name='Allen'91011#1.1定义函数12defprint_name():13p......
  • 如何实现Java中使用注解校验参数的具体操作步骤
    Java中使用注解校验参数简介在Java开发中,我们经常需要对方法的参数进行校验,以确保传入的参数符合我们的要求。而注解是一种常用的方式来实现参数校验。本文将介绍如何在Java中使用注解来校验参数,并给出一些示例代码帮助理解。整体流程下面是使用注解校验参数的整体流程,我们将通......
  • 浏览器扩展 service-worker中如何访问http请求
    参考:https://stackoverflow.com/questions/71443428/how-can-i-make-an-http-request-in-the-service-worker-chrome-extensionfetch文档:https://developer.mozilla.org/en-US/docs/Web/API/fetch因为service-worker没有没有运行到普通的环境,普通的node库也用不了,所以需要使用......
  • 在 Go gRPC 中传递额外参数
    GogRPC是一个高性能、开源的RPC框架,它支持通过ProtocolBuffers进行序列化和反序列化,提供了简单易用的API,并且可以跨语言通信。在实际开发中,我们有时需要在gRPC方法之间传递一些额外的参数,例如请求头信息、跟踪ID、取消信号等。本文将介绍在GogRPC中如何传递这些额外......