温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么用注解+RequestBodyAdvice实现http请求内容加解密方式

发布时间:2021-06-29 13:40:15 来源:亿速云 阅读:332 作者:chen 栏目:开发技术

这篇文章主要介绍“怎么用注解+RequestBodyAdvice实现http请求内容加解密方式”,在日常操作中,相信很多人在怎么用注解+RequestBodyAdvice实现http请求内容加解密方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用注解+RequestBodyAdvice实现http请求内容加解密方式”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

注解主要用来指定那些需要加解密的controller方法

实现比较简单

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface SecretAnnotation {     boolean encode() default false;     boolean decode() default false; }

使用时添加注解在controller的方法上

@PostMapping("/preview")     @SecretAnnotation(decode = true)     public ResponseVO<ContractSignVO> previewContract(@RequestBody FillContractDTO fillContractDTO)  {         return contractSignService.previewContract(fillContractDTO);     }

请求数据由二进制流转为类对象数据,对于加密过的数据,需要在二进制流被处理之前进行解密,否则在转为类对象时会因为数据格式不匹配而报错。

因此使用RequestBodyAdvice的beforeBodyRead方法来处理。

@Slf4j @RestControllerAdvice public class MyRequestControllerAdvice implements RequestBodyAdvice {     @Override     public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {         return methodParameter.hasParameterAnnotation(RequestBody.class);     }     @Override     public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {         return o;     }     @Autowired     private MySecretUtil mySecretUtil;     @Override     public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {         if (methodParameter.getMethod().isAnnotationPresent(SecretAnnotation.class)) {             SecretAnnotation secretAnnotation = methodParameter.getMethod().getAnnotation(SecretAnnotation.class);             if (secretAnnotation.decode()) {                 return new HttpInputMessage() {                     @Override                     public InputStream getBody() throws IOException {                         List<String> appIdList = httpInputMessage.getHeaders().get("appId");                         if (appIdList.isEmpty()){                             throw new RuntimeException("请求头缺少appID");                         }                         String appId = appIdList.get(0);                         String bodyStr = IOUtils.toString(httpInputMessage.getBody(),"utf-8");                         bodyStr = mySecretUtil.decode(bodyStr,appId);                         return  IOUtils.toInputStream(bodyStr,"utf-8");                     }                     @Override                     public HttpHeaders getHeaders() {                         return httpInputMessage.getHeaders();                     }                 };             }         }         return httpInputMessage;     }     @Override     public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {         return o;     } }

mySecretUtil.decode(bodyStr,appId)的内容是,通过请求头中的AppID去数据库中查找对于的秘钥,之后进行解密,返回解密后的字符串。

再通过common.io包中提供的工具类IOUtils将字符串转为inputstream流,替换HttpInputMessage,返回一个body数据为解密后的二进制流的HttpInputMessage。

Stringboot RequestBodyAdvice接口如何实现请求响应加解密

在实际项目中,我们常常需要在请求前后进行一些操作,比如:参数解密/返回结果加密,打印请求参数和返回结果的日志等。这些与业务无关的东西,我们不希望写在controller方法中,造成代码重复可读性变差。这里,我们讲讲使用@ControllerAdvice和RequestBodyAdvice、ResponseBodyAdvice来对请求前后进行处理(本质上就是AOP),来实现日志记录每一个请求的参数和返回结果。

1.加解密工具类

package com.linkus.common.utils; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; import javax.annotation.PostConstruct; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Aes {     /**      *      * @author ngh      * AES128 算法      *      * CBC 模式      *      * PKCS7Padding 填充模式      *      * CBC模式需要添加偏移量参数iv,必须16位      * 密钥 sessionKey,必须16位      *      * 介于java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 没有什么区别      * 要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现      */     private String sessionKey="加解密密钥";     // 偏移量 16位     private static  String iv="偏移量";     // 算法名称     final String KEY_ALGORITHM = "AES";     // 加解密算法/模式/填充方式     final String algorithmStr = "AES/CBC/PKCS7Padding";     // 加解密 密钥 16位     byte[] ivByte;     byte[] keybytes;     private Key key;     private Cipher cipher;     boolean isInited = false;     public void init() {         // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要         keybytes = iv.getBytes();         ivByte = iv.getBytes();         Security.addProvider(new BouncyCastleProvider());         // 转化成JAVA的密钥格式         key = new SecretKeySpec(keybytes, KEY_ALGORITHM);         try {             // 初始化cipher             cipher = Cipher.getInstance(algorithmStr, "BC");         } catch (NoSuchAlgorithmException e) {             // TODO Auto-generated catch block             e.printStackTrace();         } catch (NoSuchPaddingException e) {             // TODO Auto-generated catch block             e.printStackTrace();         } catch (NoSuchProviderException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }     }     /**      * 加密方法      *      * @param content      *            要加密的字符串      *            加密密钥      * @return      */     public String encrypt(String content) {         byte[] encryptedText = null;         byte[] contentByte = content.getBytes();         init();         try {             cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivByte));             encryptedText = cipher.doFinal(contentByte);         } catch (Exception e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         return new String(Hex.encode(encryptedText));     }     /**      * 解密方法      *      * @param encryptedData      *            要解密的字符串      *            解密密钥      * @return      */     public String decrypt(String encryptedData) {         byte[] encryptedText = null;         byte[] encryptedDataByte = Hex.decode(encryptedData);         init();         try {             cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivByte));             encryptedText = cipher.doFinal(encryptedDataByte);         } catch (Exception e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         return new String(encryptedText);     }     public static void main(String[] args) {         Aes aes = new Aes();         String a="{\n" +                 "\"distance\":\"1000\",\n" +                 "\"longitude\":\"28.206471\",\n" +                 "\"latitude\":\"112.941301\"\n" +                 "}";         //加密字符串         //String content = "孟飞快跑";         // System.out.println("加密前的:" + content); //        System.out.println("加密密钥:" + new String(keybytes));         // 加密方法         String enc = aes.encrypt(a);         System.out.println("加密后的内容:" + enc);         String dec="";         // 解密方法         try {             dec = aes.decrypt(enc);         } catch (Exception e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         System.out.println("解密后的内容:" + dec);     } }

2.请求解密

前端页面传过来的是密文,我们需要在Controller获取请求之前对密文解密然后传给Controller

package com.linkus.common.filter; import com.alibaba.fastjson.JSON; import com.linkus.common.constant.KPlatResponseCode; import com.linkus.common.exception.CustomException; import com.linkus.common.exception.JTransException; import com.linkus.common.service.util.MyHttpInputMessage; import com.linkus.common.utils.Aes; import com.linkus.common.utils.http.HttpHelper; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.lang.reflect.Type; /**  * 请求参数 解密操作  *  * @Author: Java碎碎念  * @Date: 2019/10/24 21:31  *  */ @Component //可以配置指定需要解密的包,支持多个 @ControllerAdvice(basePackages = {"com.linkus.project"}) @Slf4j public class DecryptRequestBodyAdvice implements RequestBodyAdvice {     Logger log = LoggerFactory.getLogger(getClass());     Aes aes=new Aes();     @Override     public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {     	//true开启功能,false关闭这个功能         return true;     } //在读取请求之前做处理     @Override     public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {         //获取请求数据         String string = "";         BufferedReader bufferedReader = null;         InputStream inputStream = inputMessage.getBody();             //这个request其实就是入参 可以从这里获取流             //入参放在HttpInputMessage里面  这个方法的返回值也是HttpInputMessage             try {             string=getRequestBodyStr(inputStream,bufferedReader);         } finally {             if (bufferedReader != null) {                 try {                     bufferedReader.close();                 } catch (IOException ex) {                     throw ex;                 }             }         }         /*****************进行解密start*******************/         String decode = null;         if(HttpHelper.isEncrypted(inputMessage.getHeaders())){         try {             //            //解密操作             //Map<String,String> dataMap = (Map)body;             //log.info("接收到原始请求数据={}", string);             // inputData 为待加解密的数据源	//解密             decode= aes.decrypt(string);             //log.info("解密后数据={}",decode);         } catch (Exception e ) {             log.error("加解密错误:",e);            throw  new CustomException(KPlatResponseCode.MSG_DECRYPT_TIMEOUT,KPlatResponseCode.CD_DECRYPT_TIMEOUT);         }         //把数据放到我们封装的对象中         }else{             decode = string;         }        // log.info("接收到请求数据={}", decode); //        log.info("接口请求地址{}",((HttpServletRequest)inputMessage).getRequestURI());         return new MyHttpInputMessage(inputMessage.getHeaders(), new ByteArrayInputStream(decode.getBytes("UTF-8")));     }     @Override     public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {         return body;     }     @Override     public Object handleEmptyBody(@Nullable Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<? extends HttpMessageConverter<?>> var5) {         return var1;     }	//自己写的方法,不是接口的方法,处理密文     public String getRequestBodyStr( InputStream inputStream,BufferedReader bufferedReader) throws IOException {         StringBuilder stringBuilder = new StringBuilder();             if (inputStream != null) {                 bufferedReader = new BufferedReader(new InputStreamReader(inputStream));                 char[] charBuffer = new char[128];                 int bytesRead = -1;                 while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {                     stringBuilder.append(charBuffer, 0, bytesRead);                 }             } else {                 stringBuilder.append("");             }         String string = stringBuilder.toString();         return string;     } }

3.响应加密

将返给前端的响应加密,保证数据的安全性

package com.linkus.common.filter; import com.alibaba.fastjson.JSON; import com.linkus.common.utils.Aes; import com.linkus.common.utils.DesUtil; import com.linkus.common.utils.http.HttpHelper; import io.swagger.models.auth.In; import lombok.experimental.Helper; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /**  * 请求参数 加密操作  *  * @Author: Java碎碎念  * @Date: 2019/10/24 21:31  *  */ @Component @ControllerAdvice(basePackages = {"com.linkus.project"}) @Slf4j public class EncryResponseBodyAdvice implements ResponseBodyAdvice<Object> {     Logger log = LoggerFactory.getLogger(getClass());     Aes aes=new Aes();     @Override     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {         return true;     }          @Override     public Object beforeBodyWrite(Object obj, MethodParameter returnType, MediaType selectedContentType,                                   Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest serverHttpRequest,                                   ServerHttpResponse serverHttpResponse) {         String returnStr = "";         Object retObj = null;         log.info("接口请求地址{}",serverHttpRequest.getURI());         //日志过滤         //retObj=infofilter.getInfoFilter(returnType,obj);         if(HttpHelper.isEncrypted(serverHttpRequest.getHeaders())) {             try {                 //添加encry header,告诉前端数据已加密                 //serverHttpResponse.getHeaders().add("infoe", "e=a");                 //获取请求数据                 String srcData = JSON.toJSONString(obj);                 //加密                 returnStr = aes.encrypt(srcData).replace("\r\n", "");                 //log.info("原始数据={},加密后数据={}", obj, returnStr);                 return returnStr;             } catch (Exception e) {                 log.error("异常!", e);             }         }         log.info("原始数据={}",JSON.toJSONString(obj));         return obj;     } }

到此,关于“怎么用注解+RequestBodyAdvice实现http请求内容加解密方式”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI