飞道的博客

springboot2.X手册:防抓包?快速实现API接口数据加密

467人阅读  评论(0)

有没有遇到这样子的接口,放到互联网上面去,谁都可以调用,谁都可以访问,完全就是公开的,这样子的接口,如果只是普通的数据,其实可以考虑,只是可以考虑,但是,一般情况下,我们是不允许这样子做的。

接口安全防什么

1、防止恶意调用攻击

2、防止篡改信息攻击

3、防拦截攻击,数据被截取后进行修改后重新放回去

4、防止数据泄漏攻击

什么是抓包

抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。

这是百度百科给我们的解释,当我们一些放到互联网上的数据,直接采用明文的话,就很容易被抓包,然后进行修改或者被恶意植入木马,这是比较恶心的行为,今天我们就来研究一下怎么样对接口进行数据加密。

POM文件


   
  1.  <!-- springboot核心web -->
  2.         <dependency>
  3.             <groupId>org.springframework.boot</groupId>
  4.             <artifactId>spring-boot-starter-web</artifactId>
  5.         </dependency>
  6.         <!-- 配置包,用于配置属性文件 -->
  7.         <dependency>
  8.             <groupId>org.springframework.boot</groupId>
  9.             <artifactId>spring-boot-autoconfigure</artifactId>
  10.         </dependency>
  11.         <!-- 统一API包 -->
  12.         <dependency>
  13.    <groupId>com.boots</groupId>
  14.    <artifactId>module-boots-api</artifactId>
  15.    <version> 2.0 .0.RELEASE</version>
  16.   </dependency>

编写加密解密工具类


   
  1. /**
  2.  * All rights Reserved, Designed By 林溪
  3.  * Copyright:    Copyright(C) 2016-2020
  4.  * Company       溪云阁 .
  5.  */
  6. package com.module.boots.api.de.utils;
  7. import java.security.NoSuchAlgorithmException;
  8. import java.security.SecureRandom;
  9. import javax.crypto.Cipher;
  10. import javax.crypto.KeyGenerator;
  11. import javax.crypto.SecretKey;
  12. import javax.crypto.spec.SecretKeySpec;
  13. import org.apache.tomcat.util.codec.binary.Base64;
  14. import com.module.boots.exception.CommonRuntimeException;
  15. /**
  16.  * AES加密解密
  17.  * @author:溪云阁
  18.  * @date:2020年6月4日
  19.  */
  20. public class AesUtils {
  21.     private static final String KEY_ALGORITHM =  "AES";
  22.     private static final String DEFAULT_CIPHER_ALGORITHM =  "AES/ECB/PKCS5Padding"; // 默认的加密算法
  23.      /**
  24.      * AES 加密操作
  25.      * @author 溪云阁
  26.      * @param content 待加密内容
  27.      * @param password 加密密码
  28.      * @return String 返回Base64转码后的加密数据
  29.      */
  30.     public static String encrypt(String content, String password) {
  31.         try {
  32.              // 创建密码器
  33.             final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
  34.              // 设置为UTF-8编码
  35.             final  byte[] byteContent = content.getBytes( "utf-8");
  36.              // 初始化为加密模式的密码器
  37.             cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));
  38.              // 加密
  39.             final  byte[] result = cipher.doFinal(byteContent);
  40.              // 通过Base64转码返回
  41.              return Base64.encodeBase64String(result);
  42.         }
  43.         catch (final Exception ex) {
  44.             throw  new CommonRuntimeException(ex.fillInStackTrace());
  45.         }
  46.     }
  47.      /**
  48.      * AES 解密操作
  49.      * @author 溪云阁
  50.      * @param content
  51.      * @param password
  52.      * @return String
  53.      */
  54.     public static String decrypt(String content, String password) {
  55.         try {
  56.              // 实例化
  57.             final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
  58.              // 使用密钥初始化,设置为解密模式
  59.             cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
  60.              // 执行操作
  61.             final  byte[] result = cipher.doFinal(Base64.decodeBase64(content));
  62.              // 采用UTF-8编码转化为字符串
  63.              return  new String(result,  "utf-8");
  64.         }
  65.         catch (final Exception ex) {
  66.             throw  new CommonRuntimeException(ex.fillInStackTrace());
  67.         }
  68.     }
  69.      /**
  70.      * 生成加密秘钥
  71.      * @author 溪云阁
  72.      * @param password 加密的密码
  73.      * @return SecretKeySpec
  74.      */
  75.     private static SecretKeySpec getSecretKey(final String password) {
  76.          // 返回生成指定算法密钥生成器的 KeyGenerator 对象
  77.         KeyGenerator kg = null;
  78.         try {
  79.             kg = KeyGenerator.getInstance(KEY_ALGORITHM);
  80.              // AES 要求密钥长度为 128
  81.             kg.init( 128new SecureRandom(password.getBytes()));
  82.              // 生成一个密钥
  83.             final SecretKey secretKey = kg.generateKey();
  84.              // 转换为AES专用密钥
  85.              return  new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
  86.         }
  87.         catch (final NoSuchAlgorithmException ex) {
  88.             throw  new CommonRuntimeException(ex.fillInStackTrace());
  89.         }
  90.     }
  91.     public static void main(String[] args) {
  92.         final String str =  "V9JofCHn02eyXRiDb1VuseRSuOgEQftROwudMPWwMAO2Wk5K7aYZ4Vtm6xiTn5i5";
  93.         System.out. println(decrypt(str,  "xy934yrn9342u0ry4br8cn-9u2"));
  94.     }
  95. }

编写加密注解


   
  1. /**
  2.  * All rights Reserved, Designed By 林溪
  3.  * Copyright:    Copyright(C) 2016-2020
  4.  * Company       溪云阁 .
  5.  */
  6. package com.module.boots.api.de;
  7. import java.lang.annotation.Documented;
  8. import java.lang.annotation.ElementType;
  9. import java.lang.annotation.Retention;
  10. import java.lang.annotation.RetentionPolicy;
  11. import java.lang.annotation.Target;
  12. /**
  13.  * 返回对body加密,针对类跟方法
  14.  * @author:溪云阁
  15.  * @date:2020年6月4日
  16.  */
  17. @Target({ ElementType.METHOD, ElementType.TYPE })
  18. @Retention(RetentionPolicy.RUNTIME)
  19. @Documented
  20. public @ interface ResponseEncrypt {
  21.      /**
  22.      * 返回对body加密,默认是true
  23.      * @author 溪云阁
  24.      * @return boolean
  25.      */
  26.     boolean value()  default  true;
  27. }

编写加密判断类


   
  1. /**
  2.  * All rights Reserved, Designed By 林溪
  3.  * Copyright:    Copyright(C) 2016-2020
  4.  * Company       溪云阁 .
  5.  */
  6. package com.module.boots.api.de;
  7. import org.springframework.core.MethodParameter;
  8. /**
  9.  * 是否需要加密解密
  10.  * @author:溪云阁
  11.  * @date:2020年6月4日
  12.  */
  13. public class NeedDe {
  14.      /**
  15.      * 判断是否需要加密
  16.      * @author 溪云阁
  17.      * @param returnType
  18.      * @return boolean
  19.      */
  20.     public static boolean needEncrypt(MethodParameter returnType) {
  21.         boolean encrypt =  false;
  22.          // 获取类上的注解
  23.         final boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(ResponseEncrypt.class);
  24.          // 获取方法上的注解
  25.         final boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(ResponseEncrypt.class);
  26.          if (classPresentAnno) {
  27.              // 类上标注的是否需要加密
  28.             encrypt = returnType.getContainingClass().getAnnotation(ResponseEncrypt.class).value();
  29.              // 类不加密,所有都不加密
  30.              if (!encrypt) {
  31.                  return  false;
  32.             }
  33.         }
  34.          if (methodPresentAnno) {
  35.              // 方法上标注的是否需要加密
  36.             encrypt = returnType.getMethod().getAnnotation(ResponseEncrypt.class).value();
  37.         }
  38.          return encrypt;
  39.     }
  40. }

编写加密拦截


   
  1. /**
  2.  * All rights Reserved, Designed By 林溪
  3.  * Copyright:    Copyright(C) 2016-2020
  4.  * Company       溪云阁 .
  5.  */
  6. package com.module.boots.api.de;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.core.MethodParameter;
  9. import org.springframework.http.MediaType;
  10. import org.springframework.http.converter.HttpMessageConverter;
  11. import org.springframework.http.server.ServerHttpRequest;
  12. import org.springframework.http.server.ServerHttpResponse;
  13. import org.springframework.web.bind.annotation.ControllerAdvice;
  14. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  15. import com.module.boots.api.de.utils.AesUtils;
  16. import com.module.boots.api.message.ResponseMsg;
  17. /**
  18.  * 对接口数据进行加密
  19.  * @author:溪云阁
  20.  * @date:2020年6月4日
  21.  */
  22. @ControllerAdvice
  23. public class ResponseEncryptAdvice implements ResponseBodyAdvice<Object> {
  24.     @Value( "${module.boots.response.aes.key}")
  25.     private String key;
  26.     @Override
  27.     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  28.          return  true;
  29.     }
  30.      /**
  31.      * 在写入之前更改body的值
  32.      * @author 溪云阁
  33.      * @param body
  34.      * @param returnType
  35.      * @param selectedContentType
  36.      * @param selectedConverterType
  37.      * @param request
  38.      * @param response
  39.      * @return
  40.      * @return
  41.      */
  42.     @SuppressWarnings({  "unchecked""rawtypes" })
  43.     @Override
  44.     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  45.             Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  46.             ServerHttpResponse response) {
  47.          // 判断是否需要加密
  48.         final boolean encrypt = NeedDe.needEncrypt(returnType);
  49.          if (!encrypt) {
  50.              return body;
  51.         }  else {
  52.              // 如果body是属于ResponseMsg类型,只需要对data里面的数据进行加密即可
  53.              if (body instanceof ResponseMsg) {
  54.                 final ResponseMsg responseMsg = (ResponseMsg) body;
  55.                 final Object data = responseMsg.getData();
  56.                  if (data == null) {
  57.                      return body;
  58.                 }  else {
  59.                     responseMsg.setData(AesUtils.encrypt(data.toString(), key));
  60.                      return responseMsg;
  61.                 }
  62.             }  else {
  63.                  return body;
  64.             }
  65.         }
  66.     }
  67. }

加入密钥


   
  1. # aes的密钥
  2. module.boots.response.aes.key: xy934yrn9342u0ry4br8cn -9u2

编写加密解密接口


   
  1. /**
  2.  * All rights Reserved, Designed By 林溪
  3.  * Copyright:    Copyright(C) 2016-2020
  4.  * Company       溪云阁 .
  5.  */
  6. package com.boots.api.de.view.de.view;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.web.bind.annotation.GetMapping;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RequestParam;
  12. import org.springframework.web.bind.annotation.RestController;
  13. import com.boots.api.de.view.de.vo.GetEncryptVO;
  14. import com.module.boots.api.de.ResponseEncrypt;
  15. import com.module.boots.api.de.utils.AesUtils;
  16. import com.module.boots.api.message.ResponseMsg;
  17. import com.module.boots.api.utils.MsgUtils;
  18. import com.module.boots.exception.CommonRuntimeException;
  19. import io.swagger.annotations.Api;
  20. import io.swagger.annotations.ApiOperation;
  21. import lombok.SneakyThrows;
  22. /**
  23.  * 加密数据接口
  24.  * @author:溪云阁
  25.  * @date:2020年6月4日
  26.  */
  27. @SuppressWarnings( "deprecation")
  28. @Api(tags = {  "web服务:加密数据接口" })
  29. @RestController
  30. @RequestMapping( "view/deView")
  31. public class DeView {
  32.     @Value( "${module.boots.response.aes.key}")
  33.     private String key;
  34.      /**
  35.      * 获取加密数据
  36.      * @author 溪云阁
  37.      * @return ResponseMsg<GetDeVO>
  38.      */
  39.     @ApiOperation(value =  "获取加密数据")
  40.     @GetMapping(value =  "/getEncrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  41.     @SneakyThrows(CommonRuntimeException.class)
  42.     @ResponseEncrypt
  43.     public ResponseMsg<GetEncryptVO> getEncrypt() {
  44.         final GetEncryptVO vo =  new GetEncryptVO();
  45.         vo.setId( "b037123c");
  46.         vo.setUserName( "xnwqr98urx");
  47.          return MsgUtils.buildSuccessMsg(vo);
  48.     }
  49.      /**
  50.      * 获取解密数据
  51.      * @author 溪云阁
  52.      * @return ResponseMsg<GetDeVO>
  53.      */
  54.     @ApiOperation(value =  "获取解密数据")
  55.     @GetMapping(value =  "/getDecrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  56.     @SneakyThrows(CommonRuntimeException.class)
  57.     public ResponseMsg<String> getDecrypt(@RequestParam(value =  "content") String content) {
  58.         final String str = AesUtils.decrypt(content, key);
  59.          return MsgUtils.buildSuccessMsg(str);
  60.     }
  61. }

测试

从实验的结果上看,我们在获取数据的时候,直接对data里面的数据进行了加密,这种加密方式只有我们自己可以破解,放到网上去,即使只有密钥,也破解不了。

这里只做接口的数据的加密,生产中经常需要加入token,时间戳等进行验证,各位同学自行拓展即可。


好文章,我在看


转载:https://blog.csdn.net/weixin_46768610/article/details/109882496
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场