代码编织梦想

前言

在实际工作中为了安全,会将参数进行密文传输,这里使用的是 AES + ECB + BASE64 对称加密的加密方式,在此记录并学习一下,技术不精望大家谅解。

介绍

AES加密是一种对称加密,有对称加密自然也有非对称加密

对称加密
对称加密是一种简单快速的加密方式,它在加密和解密的时候使用的密钥是同一个,并且通常不会超过256bit,密钥越小,在加解密的时候耗时越短,但是同样的,对于数据的加密会稍弱一些。本文使用的加密方式正是对称加密

非对称加密
非对称加密提供了一种更为安全的加解密解决方案,它使用了一对密钥,也就是一个公钥和一个私钥。在加密的时候使用公钥加密,且公钥可以发给任何请求的人,而解密的时候则需要使用私钥去解密。但是相应的,加解密的速度会稍慢。

总结来说:
对称加密的速度更快,但密钥传输相对麻烦,且加密稍弱,有泄露风险;
非对称加密的安全性更高,加密方式较强,密钥传输简单,但加密速度较慢,几乎没有泄露风险,适合偶尔传输数据的请求。

Start

引入依赖

使用之前要先引入此依赖

		<dependency>
            <groupId>org.apache.directory.studio</groupId>
            <artifactId>org.apache.commons.codec</artifactId>
            <version>1.8</version>
        </dependency>

编写AES加解密工具类

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.apache.commons.codec.binary.Base64;

import java.util.HashMap;
import java.util.Map;

public class AesEncryptUtils {
    //密钥 16位 可自定义
    //注意:此处的密钥要与前端一致,否则会导致解密失败
    private static final String KEY = "Mh82Pw93Q0ePaz5";

    //算法名称/加密模式/数据填充方式
    //注意: 加密方式与数据填充方式要与前端对应,否则会加密失败
    //这里使用的是ECB加密方式,数据填充使用的是PKCS5
    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    /**
     * 加密
     * @param content 加密的字符串
     * @param encryptKey key值
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String encryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
        byte[] b = cipher.doFinal(content.getBytes("utf-8"));
        // 采用base64算法进行转码,避免出现中文乱码
        return Base64.encodeBase64String(b);

    }

    /**
     * 解密
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     * @return
     * @throws Exception
     */
    public static String decrypt(String encryptStr, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        // 根据算法名称/加密方式/填充方式初始化加密
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        // 采用base64算法进行转码,避免出现中文乱码
        byte[] encryptBytes = Base64.decodeBase64(encryptStr);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
    }

    public static String encrypt(String content) throws Exception {
        return encrypt(content, KEY);
    }
    public static String decrypt(String encryptStr) throws Exception {
        return decrypt(encryptStr, KEY);
    }
}

以上则是AES用来加解密的工具类,可写个单元测试或写个main方法来测试是否可以正常加解密成功。

自定义注解

该注解可用于方法上,对需要加密的接口进行粒子化控制

import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.*;

/**
 * 出入参加解密
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface AES {

    /**
     * 入参是否解密,默认解密
     */
    boolean inDecode() default true;

    /**
     * 出参是否加密,默认加密
     */
    boolean outEncode() default true;
}

编写请求数据解密 ControllerAdvice

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
 * 请求数据解密
 */
@ControllerAdvice(basePackages = "com.xxx.controller")
public class DecodeRequestBodyAdvice implements RequestBodyAdvice {

    private static final Logger logger = LoggerFactory.getLogger(DecodeRequestBodyAdvice.class);

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        try {
            boolean encode = false;
            if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
                //获取注解配置的包含和去除字段
                SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
                //入参是否需要解密
                encode = serializedField.inDecode();
            }
            if (encode) {
                logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");
                return new MyHttpInputMessage(inputMessage);
            } else {
                return inputMessage;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());
            return inputMessage;
        }
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    class MyHttpInputMessage implements HttpInputMessage {
        private HttpHeaders headers;

        private InputStream body;

        public MyHttpInputMessage(HttpInputMessage inputMessage) throws Exception {
            this.headers = inputMessage.getHeaders();
            String requestData = IOUtils.toString(inputMessage.getBody(), "UTF-8");
            //去除请求数据中的转义字符
            String encryptStr = easpString(requestData).replace("\"", "");
            String decrypt = AesEncryptUtils.decrypt(encryptStr);
            this.body = IOUtils.toInputStream(decrypt, "UTF-8");
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }

        /**
         * @param requestData
         * @return
         */
        public String easpString(String requestData) {
            if (requestData != null && !requestData.equals("")) {
                String s = "{\"requestData\":";
                //去除requestData中的转义字符
                String data = requestData.replaceAll("\\s*|\r|\n|\t", "");
                if (!data.startsWith(s)) {
                    throw new RuntimeException("参数【requestData】缺失异常!");
                } else {
                    int closeLen = data.length() - 1;
                    int openLen = "{\"requestData\":".length();
                    String substring = StringUtils.substring(data, openLen, closeLen);
                    return substring;
                }
            }
            return "";
        }
    }
}

编写返回数据加密 ControllerAdvice

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 返回数据加密
 */
@ControllerAdvice(basePackages = "com.xxx.controller")
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {

    private final static Logger logger = LoggerFactory.getLogger(EncodeResponseBodyAdvice.class);

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter methodParameter,
                                  MediaType mediaType,
                                  Class aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        boolean encode = false;
        if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
            //获取注解配置的包含和去除字段
            SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
            //出参是否需要加密
            encode = serializedField.outEncode();
        }
        if (encode) {
            logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
                return AesEncryptUtils.encrypt(result);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());
            }
        }
        return body;
    }
}

测试

此时我们已经编写完所有代码,我们来写一个测试类来测试加解密是否正常

interface-test
我们写了两个接口,getSecret是获取返回内容的密文,getBySecret是解析密文
此时我们使用postman来测试一下接口

getSecret

postman-getSecret
可以看到我们可以正常获取到密文,那我们使用这个密文来调用另外一个接口看是否能正确解析出密文内容

getBySecret

postman-getBySecret
可以看到密文已经被成功解析出来了,同时也是我们加密前的内容。
自此我们的加解密就全部结束啦,如果想要出参加密、入参解密的话直接加上注解,不带属性即可

注意:此处后端使用的是加密名为AES,加密方式为ECB,数据填充格式为PKCS5Padding。前后端一定要完全一致,否则会导致前后端解密失败

图表

为了便于大家理解,在这里做了两个图表参考

时序图

前端 后端 DecodeRequestBodyAdvice 业务处理 EncodeResponseBodyAdvice 发送加密数据 解密 解密后的参数 加密返回信息 返回加密信息 前端 后端 DecodeRequestBodyAdvice 业务处理 EncodeResponseBodyAdvice

流程图

发送加密信息
解密
加密
前端
后端
是否开启解密
DecodeRequestBodyAdvice
业务处理
是否开启加密
EncodeResponseBodyAdvice
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HackAzrael/article/details/127022861

对象排序 ascii码排序及遍历_thekingyu的博客-爱代码爱编程

cesi:function () { var obj = { coach_id:1, coach_ad_id:12, type:1, token:this.$cookieStore.getCookie( 'token'), timestamp:new Date().getTime(),

前后端实现aes加解密(一):java后端实现_srlay的博客-爱代码爱编程

首先需要导入一个第三方jsr包,commons-codec.jar 下面是一个写好的工具类,调用该类的方法,实现使用指定密钥对明文进行加解密: package util; import java.io.Unsuppor

前端crypto-js加密解密 后端php加密解密通信传输 aes加解密_3333333_的博客-爱代码爱编程

1 安装crypto-js npm install crypto-js 2 js端 import CryptoJS from 'crypto-js' export default { decrypt(word

使用CryptoJS中的AES实现加解密(前端后端)-爱代码爱编程

最近为了解决项目中用户名、密码明文传输的问题,使用了AES在前台加密,然后在后台解密,稍微整理了一下,记录AES前台加解密和后台java加解密,以防忘记。 前台使用CryptoJS实现AES加解密的,所以要先下载组件,下载CryptoJS-v3.1.2版本之后,文件中包含components和rollups两个文件夹,components文件夹下是单个组

AES加密前端(加密解密)、后端(加密解密)-爱代码爱编程

前端: //加密; //word:要加密的内容 //加密秘钥 function asda(word,pwd){ var key = CryptoJS.enc.Utf8.parse(pwd); var result= CryptoJS.AES.encrypt(word, key, { mode: CryptoJS.mod

js 加密以及解密-爱代码爱编程

js 加密以及解密 import CryptoJS from 'crypto-js/crypto-js' const key = CryptoJS.enc.Utf8.parse('dbbddbbd') //十六位十六进制数作为密钥 const iv = CryptoJS.enc.Utf8.parse('dbbddbbd') //十六位十六进制数作为密钥

(附示例代码)AES算法实现前端加解密&后端解密-爱代码爱编程

  问题现象: 最近在项目中需要使用加密算法,在组长的推荐下,了解了 RSA 和 AES 算法,这里先发表一下我在学习 AES算法的心得体会。 问题分析: 1.简介: AES 高级加密标准(AES,Advanced Encryption Standard) AES算法常用于微信小程序加密传输,是最常见的对称加密算法(加密和解密用相同的密钥)之一

jquery java aes_AES加密解密算法(前端后端互通)-爱代码爱编程

!function(t,n){"object"==typeof exports?module.exports=exports=n():"function"==typeof define&&define.amd?define([],n):t.CryptoJS=n()}(this,function(){var t=t||function(t,n

angular和JAVA实现aes、rsa加密解密,前后端交互,前端加解密和后端JAVA加解密实现-爱代码爱编程

今天实现了下AES和RSA加密解密,主要的功能是对前后端交互数据进行加密解密,为什么要用到两个算法呢,首先RSA默认的话加密长度是有限的100多个byte吧大约,并且需要公钥私钥,而AES加密没有限制只需要一个key就可以,所以我们使用AES来进行数据的加解密,那么Key传递是不是会暴露,所以使用RSA对Key进行加密,前端生成RSA的公钥私钥,后端生成A

crytojs加密 java解密,CryptoJS前端页面js通过AES加密后端java解密实例-爱代码爱编程

最近做一个项目的安全渗透测评,测评人员发来一份测试报告,报告明确提出不允许明文参数传输,因为数据在传输的过程中可能被拦截,被监听,所以在传输数据的时候使用数据的原始内容进行传输的话,安全隐患是非常大的。因此就要对需要传输的数据进行在客户端进行加密,然后在服务器进行解密。通过对项目的评估,上头为了节省所谓的成本,不允许使用https协议,所以只能采取客户

AES+自定义密钥实现加密解密(前端+后端)-爱代码爱编程

AES+自定义密钥实现加密解密(前端+后端) 1 背景 ​ 项目需要将手机号在后端接口加密,前端进行解密并进行手机号脱敏 ​ 注意:本案例只演示了**ECB加密方式**,其他加密方式(CBC CTR OFB CFB)暂未涉及 ​ 多说无益,show your code 2 后端实现 加密代码: import org.apache.common

react项目与后端进行AES加解密交互-爱代码爱编程

react项目后端数据加密,前端解密 在做react项目的时候涉及到了加解密,因为在安全检测的时候检测出一个接口返回的数据暴漏了IP,容易被攻击,所以后端要将返回的数据加密,前端再进行解密。 首选了RSA,前后端没有解通,换了只有AES的加解密,然而也是坎坎坷坷。 首先众所周知,前后端加解密交互要保证前后端的密钥key相同,如果是CBC模式,必须要有

Vue中结合后端实现AES加解密操作(最新)-爱代码爱编程

1、安装对应的包 终端执行命令安装对应的 crypto.js 包 npm install crypto-js --save 2、添加工具类 在  utils  文件夹下创建 secret.js 文件,放入以下内容: const CryptoJS = require('crypto-js'); //引用AES源码js // const key

springboot中的后端接口加密解密-爱代码爱编程

近期在项目中因为安全红线要求需要进行接口加解密,在此记录一下。 通过@ControllerAdvice扫描所有接口进行接口加密以及接口解密,本文选择的是AES加密,通过密匙及偏移量加密接口数据。 一.结构: //加密方法,通过@ControllerAdvice扫描所有接口,对含有@EncryptResponse注解的类或者方法进行加密 /**

前后端AES加密解密-爱代码爱编程

前后端AES加密解密 1.前端AES加密解密 1. 下载crypto-js npm install crypto-js --save 工具类import CryptoJS from 'crypto-js' const key = 'f4k9f5w7f8g4er26' // 偏移量 16位(不可随意修改,否则前后端加密解密可能失败) const

Java后端AES加密解密-爱代码爱编程

package com.zimax.cqyf.admin.util; import org.apache.tomcat.util.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.s

php后端aes加密前端解密-爱代码爱编程

项目场景: 领导要求公司后台的全部接口与前端交互时,所有参数以及返回值需要进行加密。后端语言 PHP,使用框架 tp5.1,前端vus.js,使用CryptoJS加解密 效果如下: 前端传参: 后端返回: 前端代码: 前端使用CryptoJS来加密。说明:为了数据安全性,前端需要额外传递一个参数sign给到后端,后端按前端的sign

使用aes对数据javascript前端加密和java后端解密_植发一族的博客-爱代码爱编程

在实际开发项目中,有些数据在前后端的传输过程中需要进行加密,那就需要保证前端和后端的加解密需要统一。这里给大家简单演示AES在JavaScript前端和Java后端是如何实现加密和解密的。 需要依赖js文件: ae