0


SpringBoot接口加密与解密

文章目录

一、对称/非对称加密

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

publicclassConfig{publicstaticfinalStringAES_ALGORITHM="AES/CBC/PKCS5Padding";publicstaticfinalStringRSA_ALGORITHM="RSA/ECB/OAEPWithSHA-256AndMGF1Padding";//必须是PKCS8格式publicstaticfinalStringCLIENT_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==";publicstaticfinalStringCLIENT_PUBLIC_KEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F"+"+tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI"+"fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo"+"0D3FOsbNEj80opjmtQIDAQAB";publicstaticfinalStringSERVER_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=";publicstaticfinalStringSERVER_PUBLIC_KEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxpMZQCTykdwUcUzyHgd6Q79de"+"4/F26bhIpOVCDpWNxlLQFdbGneTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0"+"UDaXyZYo8fXhfD7Aeret6/CgH1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM"+"35TZ5p530ayOJ1KPHwIDAQAB";}

2、RSA非对称加密

importjavax.crypto.Cipher;importjavax.crypto.spec.OAEPParameterSpec;importjavax.crypto.spec.PSource;importjava.security.*;importjava.security.spec.MGF1ParameterSpec;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;importorg.springframework.util.Base64Utils;publicclassRSACipher{/**
     * 获取公钥
     * @param key 密钥字符串(经过base64编码)
     * @return 公钥
     */publicstaticPublicKeygetPublicKey(String key)throwsException{// 按照X.509标准对其进行编码的密钥X509EncodedKeySpec keySpec =newX509EncodedKeySpec(Base64Utils.decode(key.getBytes()));KeyFactory keyFactory =KeyFactory.getInstance("RSA");// 生成公钥PublicKey publicKey = keyFactory.generatePublic(keySpec);return publicKey;}/**
     * 获取私钥
     * @param key 密钥字符串(经过base64编码)
     * @return 私钥
     */publicstaticPrivateKeygetPrivateKey(String key)throwsException{// 按照PKCS8格式标准对其进行编码的密钥,首先要将key进行base64解码PKCS8EncodedKeySpec keySpec =newPKCS8EncodedKeySpec(Base64Utils.decode(key.getBytes()));KeyFactory keyFactory =KeyFactory.getInstance("RSA");// 生成私钥PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;}/**
     * 加密方法
     * @param publicKey 公钥
     * @param raw       待加密明文
     * @return 加密后的密文
     */publicstaticbyte[]encrypt(String publicKey,byte[] raw)throwsException{Key key =getPublicKey(publicKey);Cipher cipher =Cipher.getInstance(Config.RSA_ALGORITHM);// 初始化
        cipher.init(Cipher.ENCRYPT_MODE, key,newOAEPParameterSpec("SHA-256","MGF1",MGF1ParameterSpec.SHA256,PSource.PSpecified.DEFAULT));byte[] encryption = cipher.doFinal(raw);// 最后将加密后的数据进行base64编码returnBase64Utils.encode(encryption);}/**
     * 解密方法
     * @param privateKey 私钥
     * @param enc  待解密密文
     * @return 解密后的明文
     */publicstaticbyte[]decrypt(String privateKey,byte[] enc)throwsException{Key key =getPrivateKey(privateKey);Cipher cipher =Cipher.getInstance(Config.RSA_ALGORITHM);// 初始化
        cipher.init(Cipher.DECRYPT_MODE, key,newOAEPParameterSpec("SHA-256","MGF1",MGF1ParameterSpec.SHA256,PSource.PSpecified.DEFAULT));// 先进行base64解密,然后解码return cipher.doFinal(Base64Utils.decode(enc));}/**
     * 签名
     * @param privateKey 私钥
     * @param content    要进行签名的内容
     * @return 签名
     */publicstaticStringsign(String privateKey,byte[] content){try{// privateKey进行base64编码,然后生成PKCS8格式私钥PKCS8EncodedKeySpec priPKCS8 =newPKCS8EncodedKeySpec(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编码返回returnnewString(Base64Utils.encode(signed));}catch(Exception e){
            e.printStackTrace();}returnnull;}/**
     * 验签
     * @param publicKey 公钥
     * @param content   要验签的内容
     * @param sign      签名
     * @return 验签结果
     */publicstaticbooleancheckSign(String publicKey,byte[] content,String sign){try{KeyFactory keyFactory =KeyFactory.getInstance("RSA");// 进行base64解码byte[] encodedKey =Base64Utils.decodeFromString(publicKey);// 生成公钥PublicKey pubKey = keyFactory.generatePublic(newX509EncodedKeySpec(encodedKey));// 签名摘要算法Signature signature =Signature.getInstance("SHA256WithRSA");// 用公钥初始化签名
            signature.initVerify(pubKey);// 使用指定的字节数组更新签名或验证
            signature.update(content);// base64解码后进行验证return signature.verify(Base64Utils.decodeFromString(sign));}catch(Exception e){
            e.printStackTrace();}returnfalse;}publicstaticvoidmain(String[] args)throwsException{//客户端代码String text ="hello";//使用服务端公钥加密byte[] encryptText =RSACipher.encrypt(Config.SERVER_PUBLIC_KEY, text.getBytes());System.out.println("加密后:\n"+newString(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"+newString(decryptText));}}

输出结果

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

3、AES对称加密

importorg.springframework.util.Base64Utils;importjavax.crypto.Cipher;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;importjava.security.SecureRandom;publicclassAESCipher{publicstaticSecureRandom random =newSecureRandom();/**
     * 获取随机16位key,key必须要是10的整数倍,否则会出错
    */publicstaticStringgetRandom(int length){StringBuilder ret =newStringBuilder();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
     */publicstaticStringencrypt(byte[] key,String raw)throwsException{// 第一次加密SecretKeySpec secretKey =newSecretKeySpec(key,"AES");byte[] enCodeFormat = secretKey.getEncoded();// 获取二次加密的keySecretKeySpec secondSecretKey =newSecretKeySpec(enCodeFormat,"AES");Cipher cipher =Cipher.getInstance(Config.AES_ALGORITHM);// 向量iv,增加加密算法的强度IvParameterSpec iv =newIvParameterSpec(key);// 初始化加密器
        cipher.init(Cipher.ENCRYPT_MODE, secondSecretKey, iv);// 加密byte[] result = cipher.doFinal(raw.getBytes());// 进行base64编码returnBase64Utils.encodeToString(result);}/**
     * 解密方法,使用key充当向量iv,增加加密算法的强度
     * @param key 密钥
     * @param enc 待解密内容
     * @return
     */publicstaticStringdecrypt(byte[] key,String enc)throwsException{SecretKeySpec secretKey =newSecretKeySpec(key,"AES");byte[] enCodeFormat = secretKey.getEncoded();// 二次加密SecretKeySpec secondSecretKey =newSecretKeySpec(enCodeFormat,"AES");Cipher cipher =Cipher.getInstance(Config.AES_ALGORITHM);IvParameterSpec iv =newIvParameterSpec(key);// 初始化
        cipher.init(Cipher.DECRYPT_MODE, secondSecretKey, iv);// 首先进行base64解码byte[] bytes =Base64Utils.decodeFromString(enc);// 解密byte[] result = cipher.doFinal(bytes);returnnewString(result);}publicstaticvoidmain(String[] args)throwsException{//客户端代码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 中给我们提供了

ResponseBodyAdvice

RequestBodyAdvice

,利用这两个工具可以对请求和响应进行预处理,非常方便。

参考:
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 封装公共相应类

publicclassRespBean{privateInteger status;privateString msg;privateObject obj;publicstaticRespBeanbuild(){returnnewRespBean();}publicstaticRespBeanok(String msg){returnnewRespBean(200, msg,null);}publicstaticRespBeanok(String msg,Object obj){returnnewRespBean(200, msg, obj);}publicstaticRespBeanerror(String msg){returnnewRespBean(500, msg,null);}publicstaticRespBeanerror(String msg,Object obj){returnnewRespBean(500, msg, obj);}privateRespBean(){}privateRespBean(Integer status,String msg,Object obj){this.status = status;this.msg = msg;this.obj = obj;}publicIntegergetStatus(){return status;}publicRespBeansetStatus(Integer status){this.status = status;returnthis;}publicStringgetMsg(){return msg;}publicRespBeansetMsg(String msg){this.msg = msg;returnthis;}publicObjectgetObj(){return obj;}publicRespBeansetObj(Object obj){this.obj = obj;returnthis;}}

2.3 定义加解密工具类

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

publicclassAESUtils{privatestaticfinalStringAES_ALGORITHM="AES/ECB/PKCS5Padding";// 获取 cipherprivatestaticCiphergetCipher(byte[] key,int model)throwsException{SecretKeySpec secretKeySpec =newSecretKeySpec(key,"AES");Cipher cipher =Cipher.getInstance(AES_ALGORITHM);
        cipher.init(model, secretKeySpec);return cipher;}// AES加密publicstaticStringencrypt(byte[] data,byte[] key)throwsException{Cipher cipher =getCipher(key,Cipher.ENCRYPT_MODE);returnBase64.getEncoder().encodeToString(cipher.doFinal(data));}// AES解密publicstaticbyte[]decrypt(byte[] data,byte[] key)throwsException{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@interfaceDecrypt{}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceEncrypt{}

2.5 设置自定义key

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

spring.encrypt.key=xxx

即可。

@ConfigurationProperties(prefix ="spring.encrypt")@ComponentpublicclassEncryptProperties{// 这一块一定要16位或者整数倍,最多256privatefinalstaticStringDEFAULT_KEY="www.shawn222.com";privateString key =DEFAULT_KEY;publicStringgetKey(){return key;}publicvoidsetKey(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)@ControllerAdvicepublicclassEncryptResponseimplementsResponseBodyAdvice<RespBean>{privateObjectMapper om =newObjectMapper();@AutowiredEncryptProperties encryptProperties;@Overridepublicbooleansupports(MethodParameter returnType,Class<?extendsHttpMessageConverter<?>> converterType){return returnType.hasMethodAnnotation(Encrypt.class);}@OverridepublicRespBeanbeforeBodyWrite(RespBean body,MethodParameter returnType,MediaType selectedContentType,Class<?extendsHttpMessageConverter<?>> 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)@ControllerAdvicepublicclassDecryptRequestextendsRequestBodyAdviceAdapter{@AutowiredEncryptProperties encryptProperties;@Overridepublicbooleansupports(MethodParameter methodParameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType){return methodParameter.hasMethodAnnotation(Decrypt.class)|| methodParameter.hasParameterAnnotation(Decrypt.class);}@OverridepublicHttpInputMessagebeforeBodyRead(finalHttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType)throwsIOException{byte[] body =newbyte[inputMessage.getBody().available()];
        inputMessage.getBody().read(body);try{byte[] decrypt =AESUtils.decrypt(body, encryptProperties.getKey().getBytes());finalByteArrayInputStream bais =newByteArrayInputStream(decrypt);returnnewHttpInputMessage(){@OverridepublicInputStreamgetBody()throwsIOException{return bais;}@OverridepublicHttpHeadersgetHeaders(){return inputMessage.getHeaders();}};}catch(Exception e){
            e.printStackTrace();}returnsuper.beforeBodyRead(inputMessage, parameter, targetType, converterType);}}

4、打包发布starter

4.1 定义自动化配置类

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

最后,

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、新项目使用

创建实体类

publicclassUser{privateLong id;privateString username;//省略 getter/setter}

创建测试类,第一个接口使用了

@Encrypt

注解,所以会对该接口的数据进行加密(如果不使用该注解就不加密),第二个接口使用了

@Decrypt

所以会对上传的参数进行解密,注意

@Decrypt

注解既可以放在方法上也可以放在参数上。

@RestControllerpublicclassHelloController{@GetMapping("/user")@EncryptpublicRespBeangetUser(){User user =newUser();
        user.setId((long)99);
        user.setUsername("javaboy");returnRespBean.ok("ok", user);}@PostMapping("/user")publicRespBeanaddUser(@RequestBody@DecryptUser user){System.out.println("user = "+ user);returnRespBean.ok("ok", user);}}

参考文章

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

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

Hutool加密解密

【网络】java密码安全


本文转载自: https://blog.csdn.net/qq_43842093/article/details/132009346
版权归原作者 Archie_java 所有, 如有侵权,请联系我们删除。

“SpringBoot接口加密与解密”的评论:

还没有评论