文章目录

  • 一、对称/非对称加密
    • 1、简介
    • 2、RSA和AES介绍
      • 2.1 RSA
      • 2.2 AES
    • 3、RSA/AES组合
    • 4、Base64编码的作用
  • 二、Java实现加解密/加验签
    • 1、全局Config
    • 2、RSA非对称加密
    • 3、AES对称加密
  • 三、加解密 starter实战
    • 1、介绍
    • 2、前期准备
      • 2.1 引入依赖
      • 2.2 封装公共相应类
      • 2.3 定义加解密工具类
      • 2.4 定义两个注解
      • 2.5 设置自定义key
    • 3、接口加密与解密
      • 3.1 介绍
      • 3.2 接口加密
      • 3.3 接口解密
    • 4、打包发布starter
      • 4.1 定义自动化配置类
      • 4.2 发布线上使用
    • 5、新项目使用

一、对称/非对称加密

1、简介

对称加密只有一个秘钥,加密和解密都是用同一个秘钥,所以叫做对称加密。

非对称加密有两个秘钥,一个是公钥,一个是私钥。非对称的特点在于,公钥加密的私钥可以解密,但私钥加密的,公钥解不出来,只能验证是否由私钥进行加密

目前常见的加密方式是有两种,一种是对称加密(AES为代表),一种是非对称加密(RSA为代表)

2、RSA和AES介绍

2.1 RSA

特点:只需交换公钥;公/秘钥机制,公钥加密,私钥解密(或者私钥加密,公钥解密);公钥负责加密,私钥负责解密;私钥负责签名,公钥负责验证

缺点:加解密速度慢,特别是解密

2.2 AES

特点:加解密用同一秘钥

优点:速度快,效率高;

缺点:秘钥交换问题

3、RSA/AES组合

对称加密(AES)的优势在于加密较快,但劣势在于秘钥一旦给出去就不安全了。非对称加密(RSA)的优势在于安全,就算提供公钥出去,别人也解密不了数据,但劣势是加密速度较慢

实际使用的过程中常常将两者组合使用(AES+RSA),这样可以安全的传输AES秘钥,避免了RSA加密的慢速度

  • 生成一个随机AES秘钥字符串

  • 使用RSA公钥加密AES秘钥,然后再用AES秘钥加密真正的内容

  • 把skey=加密的AES秘钥,body=AES秘钥加密的内容传过去

  • 对面使用RSA私钥解密AES秘钥,然后用AES秘钥解密出内容

4、Base64编码的作用

加密后的数据可能不具备可读性,因此我们一般需要对加密后的数据再使用 Base64 算法进行编码,获取可读字符串。换言之,AES 或者RSA加密方法的返回值是一个 Base64 编码之后的字符串,AES或者RSA 解密方法的参数也是一个 Base64 编码之后的字符串,先对该字符串进行解码,然后再解密。

二、Java实现加解密/加验签

1、全局Config

public class Config {public static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";public static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";//必须是PKCS8格式public static final String CLIENT_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO/8ucCgOTJ7DCPC" +"rCCL1VKDnUX61QnxwbAvpGp1/lletEIcjUouM7F0VvMHzViNLvpw7N7NBHPa+5gO" +"js68t9hKMUh+a6RTE34SWIqSDRPCzDKVWugsFb04o3vRl3rZ1z6B+QDdW7xwOhEr" +"PPoEqmjjIOjQPcU6xs0SPzSimOa1AgMBAAECgYAO5m0OBaSnerZNPhf7yVLMVbmd" +"D67MeEMjUkHuDjdlixi8BhPLqESzXtrLKg/Y0KM7D2nVh3sgSldWoIjDUzpCx8Z2" +"yHLU1K2wakMdBgEF3xeJPxxZRpP+earl0SyLTA4hMxl48uAjn/mkPgzoMgQkqyQz" +"5HOWjjsCLJFyEvqmoQJBAP5cBk0KXpHnCMgOupbi/pXDyaF1o+dCE97GaEdrV/0P" +"uwDfYDYfY3wzd1QM7C4b4MmE+SNVpC0W9PyaMONJlN0CQQDxiPiGdwX9actMNJea" +"JZ+k3BjCN+mM6Px7j/mtYcXWNZkyCXSXUBI62drZ0htenrh2qwichMlMgNJClvG6" +"Gu+5AkEA30R7q2gstrkrNh/nnMZHXcJr3DPc2QNhWayin/4TT+hc51krpJZMxxqN" +"5dMqBRcnavwzi9aCs6lxBcF6pCdUaQJANhd7uPls4PzRZ6abkQz9/LjB3rUQ29rN" +"uIpc2yR7XuawAVG2x7BJ9N4XMhLoyD75hrH1AsCGKFjtPbZ6OjiQGQJAF2DbIodC" +"uYb6eMZ8ux1Ab0wBEWWc5+iGgEVBNh22uZ/klE1/C0+KKzZhqgzaA/vPapq6dhuJ" +"sNXlJia10PwYrQ==";public static final String CLIENT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F" +"+tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI" +"fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo" +"0D3FOsbNEj80opjmtQIDAQAB";public static final String SERVER_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPGkxlAJPKR3BRxT" +"PIeB3pDv117j8XbpuEik5UIOlY3GUtAV1sad5NNDUAnP/DB80yAQ8ycm9Xdkutuo" +"f25Xlb7w0bRQNpfJlijx9eF8PsB6t63r8KAfWJlqbNHgN8AMK9P5XzVyN4YiEnUl" +"Jh/EYiwLiYzflNnmnnfRrI4nUo8fAgMBAAECgYEAvwTxm81heeV4Tcbi33/jUBG4" +"4BMzCzyA6DQp4wkiYju3tTS+Xq3seLEKcWdPxYi3YO7lODsM6j/fksrlSXXFMe1i" +"ZAF3FNuDVZPz2zdFYS8vh6kdlDHMJAUnU/POMMWJ880MQDtkwTuzH8Tao8OKcAP4" +"kc0QuG00wOrmuE+5gZECQQD9bqZkJsN+tj3+pxs57azy6B6gOqgm54/ujB+u63XU" +"rO9Sf57asgF4OfUFltaVhjlUMSrWcgp6f4HSy7hBSKJpAkEA9BeML5iDIHOgTIws" +"+ID55ELbzO7A/YtcYnUU09mkKCdonMXbXke+EhLApf5vX9ZmreoEfJCdsTnMEcQi" +"fkjkRwJBALpf2TXl2/cfhs/zjG45f+rTEVK8UFTsDklb+yDkQC87TnTZLbWfGr2T" +"wcFugDhOEXL9BYfXLiWQB6VB9Crug6ECQGEmTiFTbj0oSBCvaeauTsdO5PS3whAn" +"u2lkeBmpcfCZXsWm6hyoKTpARHTMw789Mjjd/1Mkq96xxkr76U6h7FkCQHRc2elg" +"Dh84wqHIptwa+moosVvd7aSzktuOB4CQRO10qKkSHVFuI+sl47A4KGzH/nX9ydUm" +"tpsTnQAlXwBczd4=";public static final String SERVER_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxpMZQCTykdwUcUzyHgd6Q79de" +"4/F26bhIpOVCDpWNxlLQFdbGneTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0" +"UDaXyZYo8fXhfD7Aeret6/CgH1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM" +"35TZ5p530ayOJ1KPHwIDAQAB";}

2、RSA非对称加密

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.security.*;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.springframework.util.Base64Utils;public class RSACipher {/*** 获取公钥* @param key 密钥字符串(经过base64编码)* @return 公钥*/public static PublicKey getPublicKey(String key) throws Exception {// 按照X.509标准对其进行编码的密钥X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Utils.decode(key.getBytes()));KeyFactory keyFactory = KeyFactory.getInstance("RSA");// 生成公钥PublicKey publicKey = keyFactory.generatePublic(keySpec);return publicKey;}/*** 获取私钥* @param key 密钥字符串(经过base64编码)* @return 私钥*/public static PrivateKey getPrivateKey(String key) throws Exception {// 按照PKCS8格式标准对其进行编码的密钥,首先要将key进行base64解码PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Utils.decode(key.getBytes()));KeyFactory keyFactory = KeyFactory.getInstance("RSA");// 生成私钥PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;}/*** 加密方法* @param publicKey 公钥* @param raw       待加密明文* @return 加密后的密文*/public static byte[] encrypt(String publicKey, byte[] raw) throws Exception {Key key = getPublicKey(publicKey);Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);// 初始化cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));byte[] encryption = cipher.doFinal(raw);// 最后将加密后的数据进行base64编码return Base64Utils.encode(encryption);}/*** 解密方法* @param privateKey 私钥* @param enc  待解密密文* @return 解密后的明文*/public static byte[] decrypt(String privateKey, byte[] enc) throws Exception {Key key = getPrivateKey(privateKey);Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);// 初始化cipher.init(Cipher.DECRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));// 先进行base64解密,然后解码return cipher.doFinal(Base64Utils.decode(enc));}/*** 签名* @param privateKey 私钥* @param content    要进行签名的内容* @return 签名*/public static String sign(String privateKey, byte[] content) {try {// privateKey进行base64编码,然后生成PKCS8格式私钥PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Utils.decode(privateKey.getBytes()));KeyFactory key = KeyFactory.getInstance("RSA");PrivateKey priKey = key.generatePrivate(priPKCS8);// 签名摘要算法Signature signature = Signature.getInstance("SHA256WithRSA");// 用私钥初始化此对象以进行签名signature.initSign(priKey);// 使用指定的字节数组更新签名或验证signature.update(content);// 获得签名字节byte[] signed = signature.sign();// 进行base64编码返回return new String(Base64Utils.encode(signed));} catch (Exception e) {e.printStackTrace();}return null;}/*** 验签* @param publicKey 公钥* @param content   要验签的内容* @param sign      签名* @return 验签结果*/public static boolean checkSign(String publicKey, byte[] content, String sign) {try {KeyFactory keyFactory = KeyFactory.getInstance("RSA");// 进行base64解码byte[] encodedKey = Base64Utils.decodeFromString(publicKey);// 生成公钥PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));// 签名摘要算法Signature signature = Signature.getInstance("SHA256WithRSA");// 用公钥初始化签名signature.initVerify(pubKey);// 使用指定的字节数组更新签名或验证signature.update(content);// base64解码后进行验证return signature.verify(Base64Utils.decodeFromString(sign));} catch (Exception e) {e.printStackTrace();}return false;}public static void main(String[] args) throws Exception {//客户端代码String text = "hello";//使用服务端公钥加密byte[] encryptText = RSACipher.encrypt(Config.SERVER_PUBLIC_KEY, text.getBytes());System.out.println("加密后:\n" + new String(encryptText));//使用客户端私钥签名String signature = RSACipher.sign(Config.CLIENT_PRIVATE_KEY, encryptText);System.out.println("签名:\n" + signature);//服务端代码//使用客户端公钥验签boolean result = RSACipher.checkSign(Config.CLIENT_PUBLIC_KEY, encryptText, signature);System.out.println("验签:\n" + result);//使用服务端私钥解密byte[] decryptText = RSACipher.decrypt(Config.SERVER_PRIVATE_KEY, encryptText);System.out.println("解密后:\n" + new String(decryptText));}
}

输出结果

加密后:
ODdEkwo1RgRW8UMoHXPKe9Gwcp6lTCkg4P/Ra3gfkrO+Fw6pSgo0H54nMC5sYSsoUVy1wy2/QXeLSwR6Obfl7SU7DeW+XdGee83O2kgdsDQPbYFwlPYTd0cdOmWwZxtgEOIB9d5G75Iut4kci15vrhXZVtku92U+7aNwtYimSDQ=
签名:
RL1qIScizRyu79/y+r2TN2FL/bSQDxnDj4JlDwSZM6XZR7CL7u5ZjLNHbsSYpHaCv9qKMS4ump50LyF+go05dsPjWZOvFNkgcm9LepkDP1qm8AzKdTGwlzhdBmy2397Ed8uBrQocFGj/721Y2xM/Db0nt7r54zKZkDXbMMlsd9k=
验签:
true
解密后:
hello

3、AES对称加密

import org.springframework.util.Base64Utils;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;public class AESCipher {public static SecureRandom random = new SecureRandom();/*** 获取随机16位key,key必须要是10的整数倍,否则会出错*/public static String getRandom(int length) {StringBuilder ret = new StringBuilder();for (int i = 0; i < length; i++) {// 输出字母还是数字boolean isChar = (random.nextInt(2) % 2 == 0);// 字符串if (isChar) {// 取得大写字母还是小写字母int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;ret.append((char) (choice + random.nextInt(26)));} else { // 数字ret.append(random.nextInt(10));}}return ret.toString();}/*** 加密方法,使用key充当向量iv,增加加密算法的强度* 更加安全* @param key 密钥* @param raw 需要加密的内容* @return*/public static String encrypt(byte[] key, String raw) throws Exception {// 第一次加密SecretKeySpec secretKey = new SecretKeySpec(key, "AES");byte[] enCodeFormat = secretKey.getEncoded();// 获取二次加密的keySecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);// 向量iv,增加加密算法的强度IvParameterSpec iv = new IvParameterSpec(key);// 初始化加密器cipher.init(Cipher.ENCRYPT_MODE, secondSecretKey, iv);// 加密byte[] result = cipher.doFinal(raw.getBytes());// 进行base64编码return Base64Utils.encodeToString(result);}/*** 解密方法,使用key充当向量iv,增加加密算法的强度* @param key 密钥* @param enc 待解密内容* @return*/public static String decrypt(byte[] key, String enc) throws Exception {SecretKeySpec secretKey = new SecretKeySpec(key, "AES");byte[] enCodeFormat = secretKey.getEncoded();// 二次加密SecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);IvParameterSpec iv = new IvParameterSpec(key);// 初始化cipher.init(Cipher.DECRYPT_MODE, secondSecretKey, iv);// 首先进行base64解码byte[] bytes = Base64Utils.decodeFromString(enc);// 解密byte[] result = cipher.doFinal(bytes);return new String(result);}public static void main(String[] args) throws Exception {//客户端代码String text = "hello";//随机生成16位aes密钥,也可以自己指定16位byte[] aesKey = getRandom(16).getBytes();String encryptText = AESCipher.encrypt(aesKey, text);System.out.println("加密后:\n" + encryptText);String decryptText = AESCipher.decrypt(aesKey, encryptText);System.out.println("解密后:\n" + decryptText);}
}

输出结果

加密后:
hwkYAF9eXj/dytmDBD30xg==
解密后:
hello

三、加解密 starter实战

1、介绍

加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据。不过 SpringMVC 中给我们提供了 ResponseBodyAdviceRequestBodyAdvice,利用这两个工具可以对请求和响应进行预处理,非常方便。

参考:
RSA+AES混合加密-JavaWebSpringBoot自定义starter

2、前期准备

2.1 引入依赖

因为我们这个工具是为 Web 项目开发的,以后必然使用在 Web 环境中,所以这里添加依赖时 scope 设置为 provided

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><scope>provided</scope><version>2.7.0</version>
</dependency>

scope几个属性介绍

  • compile:默认值 他表示被依赖项目需要参与当前项目的编译,还有后续的测试,运行周期也参与其中,是一个比较强的依赖。打包的时候通常需要包含进去

  • test:依赖项目仅仅参与测试相关的工作,包括测试代码的编译和执行,不会被打包,例如:junit

  • runtime:表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过了编译而已。例如JDBC驱动,适用运行和测试阶段

  • provided:打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是打包阶段做了exclude操作

  • system:从参与度来说,和provided相同,不过被依赖项不会从maven仓库下载,而是从本地文件系统拿。需要添加systemPath的属性来定义路径

2.2 封装公共相应类

public class RespBean {private Integer status;private String msg;private Object obj;public static RespBean build() {return new RespBean();}public static RespBean ok(String msg) {return new RespBean(200, msg, null);}public static RespBean ok(String msg, Object obj) {return new RespBean(200, msg, obj);}public static RespBean error(String msg) {return new RespBean(500, msg, null);}public static RespBean error(String msg, Object obj) {return new RespBean(500, msg, obj);}private RespBean() {}private RespBean(Integer status, String msg, Object obj) {this.status = status;this.msg = msg;this.obj = obj;}public Integer getStatus() {return status;}public RespBean setStatus(Integer status) {this.status = status;return this;}public String getMsg() {return msg;}public RespBean setMsg(String msg) {this.msg = msg;return this;}public Object getObj() {return obj;}public RespBean setObj(Object obj) {this.obj = obj;return this;}
}

2.3 定义加解密工具类

加密这块有多种方案可以选择,对称加密、非对称加密,其中对称加密又可以使用 AES、DES、3DES 等不同算法,这里我们使用 Java 自带的 Cipher 来实现对称加密,使用 AES 算法

public class AESUtils {private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";// 获取 cipherprivate static Cipher getCipher(byte[] key, int model) throws Exception {SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");Cipher cipher = Cipher.getInstance(AES_ALGORITHM);cipher.init(model, secretKeySpec);return cipher;}// AES加密public static String encrypt(byte[] data, byte[] key) throws Exception {Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);return Base64.getEncoder().encodeToString(cipher.doFinal(data));}// AES解密public static byte[] decrypt(byte[] data, byte[] key) throws Exception {Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);return cipher.doFinal(Base64.getDecoder().decode(data));}
}

2.4 定义两个注解

接下来我们定义两个注解 @Decrypt@Encrypt。在以后使用的过程中,哪个接口方法添加了 @Encrypt 注解就对哪个接口的数据加密返回,哪个接口/参数添加了 @Decrypt 注解就对哪个接口/参数进行解密。另外就是 @Decrypt 可以用在参数上

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {}

2.5 设置自定义key

定义一个 EncryptProperties 类来读取用户配置的 key,这样就可以自定义key。这里设置了默认值,以后如果用户想自己配置 key,只需要在 application.properties 中配置 spring.encrypt.key=xxx 即可。

@ConfigurationProperties(prefix = "spring.encrypt")
@Component
public class EncryptProperties {// 这一块一定要16位或者整数倍,最多256private final static String DEFAULT_KEY = "www.shawn222.com";private String key = DEFAULT_KEY;public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}

3、接口加密与解密

3.1 介绍

ResponseBodyAdvice 在你使用了 @ResponseBody 注解的时候才会生效,RequestBodyAdvice 在你使用了 @RequestBody 注解的时候才会生效,换言之,前后端都是 JSON 交互的时候,这两个才有用

3.2 接口加密

我们自定义 EncryptResponse 类实现 ResponseBodyAdvice接口,泛型表示接口的返回类型,这里一共要实现两个方法

  • supports:这个方法用来判断什么样的接口需要加密,参数 returnType 表示返回类型,我们这里的判断逻辑就是方法是否含有 @Encrypt 注解,如果有,表示该接口需要加密处理,如果没有,表示该接口不需要加密处理。

  • beforeBodyWrite:这个方法会在数据响应之前执行,也就是我们先对响应数据进行二次处理,处理完成后,才会转成 json 返回。我们这里的处理方式很简单,RespBean 中的 status 是状态码就不用加密了,另外两个字段重新加密后重新设置值即可。

另外需要注意,自定义的 ResponseBodyAdvice 需要用 @ControllerAdvice 注解来标记。

@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<RespBean> {private ObjectMapper om = new ObjectMapper();@AutowiredEncryptProperties encryptProperties;@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return returnType.hasMethodAnnotation(Encrypt.class);}@Overridepublic RespBean beforeBodyWrite(RespBean body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {byte[] keyBytes = encryptProperties.getKey().getBytes();try {if (body.getMsg()!=null) {body.setMsg(AESUtils.encrypt(body.getMsg().getBytes(),keyBytes));}if (body.getObj() != null) {body.setObj(AESUtils.encrypt(om.writeValueAsBytes(body.getObj()), keyBytes));}} catch (Exception e) {e.printStackTrace();}return body;}
}

3.3 接口解密

首先大家注意,DecryptRequest 类我们没有直接实现 RequestBodyAdvice 接口,而是继承自 RequestBodyAdviceAdapter 类,该类是 RequestBodyAdvice 接口的子类,并且实现了接口中的一些方法,这样当我们继承自 RequestBodyAdviceAdapter 时,就只需要根据自己实际需求实现某几个方法即可。

  • supports:该方法用来判断哪些接口需要处理接口解密,我们这里的判断逻辑是方法上或者参数上含有 @Decrypt 注解的接口,处理解密问题。

  • beforeBodyRead:这个方法会在参数转换成具体的对象之前执行,我们先从流中加载到数据,然后对数据进行解密,解密完成后再重新构造 HttpInputMessage 对象返回。

@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {@AutowiredEncryptProperties encryptProperties;@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);}@Overridepublic HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {byte[] body = new byte[inputMessage.getBody().available()];inputMessage.getBody().read(body);try {byte[] decrypt = AESUtils.decrypt(body, encryptProperties.getKey().getBytes());final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);return new HttpInputMessage() {@Overridepublic InputStream getBody() throws IOException {return bais;}@Overridepublic HttpHeaders getHeaders() {return inputMessage.getHeaders();}};} catch (Exception e) {e.printStackTrace();}return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);}
}

4、打包发布starter

4.1 定义自动化配置类

// 换成自己的包路径
@Configuration
@ComponentScan("com.example.encryption")
public class EncryptAutoConfiguration {}

最后,resources 目录下定义 META-INF,然后再定义 spring.factories 文件,这样当项目启动时,就会自动加载该配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.encryption.EncryptAutoConfiguration

安装到本地仓库比较简单,直接 mvn install,或者在 IDEA 中,点击右边的 Maven,然后双击 install

4.2 发布线上使用

发不到线上我们可以使用 JitPack来做。首先我们在 GitHub 上创建一个仓库,将我们的代码上传上去,上传成功后,点击右边的 Create a new release 按钮,发布一个正式版

发布成功后,打开 jitpack,输入仓库的完整路径,点击 lookup 按钮,查找到之后,再点击 Get it 按钮完成构建,构建成功后,JitPack 上会给出项目引用方式,新建项目时引入即可

5、新项目使用

创建实体类

public class User {private Long id;private String username;//省略 getter/setter
}

创建测试类,第一个接口使用了 @Encrypt 注解,所以会对该接口的数据进行加密(如果不使用该注解就不加密),第二个接口使用了 @Decrypt 所以会对上传的参数进行解密,注意 @Decrypt 注解既可以放在方法上也可以放在参数上。

@RestController
public class HelloController {@GetMapping("/user")@Encryptpublic RespBean getUser() {User user = new User();user.setId((long) 99);user.setUsername("javaboy");return RespBean.ok("ok", user);}@PostMapping("/user")public RespBean addUser(@RequestBody @Decrypt User user) {System.out.println("user = " + user);return RespBean.ok("ok", user);}
}

参考文章

如何优雅的实现 SpringBoot 接口参数加密解密?

为什么使用 Java Cipher 要指定转换模式?

Hutool加密解密

【网络】java密码安全

SpringBoot接口加密与解密相关推荐

  1. SpringBoot 接口加密解密,新姿势!

    我是 ABin-阿斌:写一生代码,创一世佳话,筑一览芳华.如果小伙伴们觉得不错就一键三连吧~ 声明:原文作者:微信公众号:方志朋 文章目录 1 . 介绍 2 . 前置知识 2.1 hutool-cry ...

  2. SpringBoot 接口加密解密,新姿势

    1. 介绍 在我们日常的Java开发中,免不了和其他系统的业务交互,或者微服务之间的接口调用 如果我们想保证数据传输的安全,对接口出参加密,入参解密. 但是不想写重复代码,我们可以提供一个通用star ...

  3. SpringBoot 接口数据加解密技巧

    对接口整体进行加密处理 需求解析: 服务端.客户端和H5统一拦截加解密,网上有成熟方案,也可以按其他服务中实现的加解密流程来搞: 使用AES放松加密,考虑到H5端存储密钥安全性相对来说会低一些,故分针 ...

  4. 【转载】SpringBoot 接口数据加解密技巧,so easy!

    在不影响原代码逻辑下,梳理了相关技术方案,主要的需求点如下: 尽量少改动,不影响之前的业务逻辑: 考虑到时间紧迫性,可采用对称性加密方式,服务需要对接安卓.IOS.H5三端,另外考虑到H5端存储密钥安 ...

  5. SpringBoot 接口数据加解密

    xx项目有于安全问题,需要对接口整体进行加密处理,我们怎么处理呢. 和产品.前端讨论需求后,梳理了相关技术方案,主要的需求点如下: 尽量少改动,不影响之前的业务逻辑: 考虑到时间紧迫性,可采用对称性加 ...

  6. SpringBoot 接口数据加解密技巧,so easy!

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 每天 14:00 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java ...

  7. SpringBoot 接口数据加解密实战!

    这日,刚撸完2行代码,正准备掏出手机摸鱼放松放松,只见老大朝我走过来,并露出一个"善意"的微笑,兴伟呀,xx项目有于安全问题,需要对接口整体进行加密处理,你这方面比较有经验,就给你 ...

  8. SpringBoot 接口数据加解密实战

    这日,刚撸完2行代码,正准备掏出手机摸鱼放松放松,只见老大朝我走过来,并露出一个"善意"的微笑,兴伟呀,xx项目有于安全问题,需要对接口整体进行加密处理,你这方面比较有经验,就给你 ...

  9. SpringBoot接口数据加解密实战

    主要的需求点如下: 尽量少改动,不影响之前的业务逻辑: 考虑到时间紧迫性,可采用对称性加密方式,服务需要对接安卓.IOS.H5三端,另外考虑到H5端存储密钥安全性相对来说会低一些,故分针对H5和安卓. ...

  10. java .net 加密解密,【汉字加密解密】一个16进制,可用于.net与java接口加密、解密...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 /** * 得到16进制的GBK编码,转换为汉字 * @param hexStr * @return */ public static String de ...

最新文章

  1. Linux使用dd命令快速生成大文件
  2. 【快乐水题】1688. 比赛中的配对次数
  3. hash hashcode变化_没想到 Hash 冲突还能这么玩,你的服务中招了吗?
  4. 对struts一点理解总结
  5. 数据库管家----ADODB类库.
  6. Intel 64/x86_64/IA-32/x86处理器 - 锁原子操作(1) - 处理器保证的原子操作
  7. CSS Margin(外边距)
  8. Java--UI--弹出对话框
  9. 基于遗传算法的车辆优化调度-matlab代码 考虑供应过剩惩罚、供应不足惩罚成本
  10. iframe是什么?iframe用法详解。
  11. typedef struct Lnode{ Elemtype data; struct Lnode *next; } Lnode,*Linklist;
  12. 主机电源供电端口-24PIN
  13. python无限循环怎么结束,python的无限循环及退出
  14. 黑客组织“洋葱狗”潜伏3年终曝光 定期偷袭能源及交通行业
  15. pod2g宣布A5的Sandbox破解成功
  16. Gentoo Linux+KDE Plasma桌面安装教程
  17. 基于Scrapy框架爬取豆瓣《复联4》影评,并生成词云
  18. pycharm安装mysql驱动包
  19. bernoulli vs binominal vs multinoulli vs multinomial
  20. 初学前端,学习路线图必不可少,更有【95页】初级前端模块笔记!

热门文章

  1. Axure9桌面无法显示图标
  2. 20000个游戏娱乐音效素材库
  3. AVL Cruise 2020安装教程
  4. Carrot2 聚类算法概要说明
  5. 应急响应常用的工具说明
  6. 电信iptv机顶盒破解方法大全(图文教程)
  7. 计算机组成原理试题和答案2017,【2017年整理】计算机组成原理试题及答案9.doc...
  8. 深度学习 和 技术开锁
  9. Genius‘s Gambit【学习进度条1】
  10. vscode风格超酷个人主页源码