您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么用注解+RequestBodyAdvice實(shí)現(xiàn)http請(qǐng)求內(nèi)容加解密方式”,在日常操作中,相信很多人在怎么用注解+RequestBodyAdvice實(shí)現(xiàn)http請(qǐng)求內(nèi)容加解密方式問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么用注解+RequestBodyAdvice實(shí)現(xiàn)http請(qǐng)求內(nèi)容加解密方式”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
實(shí)現(xiàn)比較簡(jiǎn)單
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface SecretAnnotation { boolean encode() default false; boolean decode() default false; }
@PostMapping("/preview") @SecretAnnotation(decode = true) public ResponseVO<ContractSignVO> previewContract(@RequestBody FillContractDTO fillContractDTO) { return contractSignService.previewContract(fillContractDTO); }
請(qǐng)求數(shù)據(jù)由二進(jìn)制流轉(zhuǎn)為類(lèi)對(duì)象數(shù)據(jù),對(duì)于加密過(guò)的數(shù)據(jù),需要在二進(jìn)制流被處理之前進(jìn)行解密,否則在轉(zhuǎn)為類(lèi)對(duì)象時(shí)會(huì)因?yàn)閿?shù)據(jù)格式不匹配而報(bào)錯(cuò)。
因此使用RequestBodyAdvice的beforeBodyRead方法來(lái)處理。
@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("請(qǐng)求頭缺少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)的內(nèi)容是,通過(guò)請(qǐng)求頭中的AppID去數(shù)據(jù)庫(kù)中查找對(duì)于的秘鑰,之后進(jìn)行解密,返回解密后的字符串。
再通過(guò)common.io包中提供的工具類(lèi)IOUtils將字符串轉(zhuǎn)為inputstream流,替換HttpInputMessage,返回一個(gè)body數(shù)據(jù)為解密后的二進(jìn)制流的HttpInputMessage。
在實(shí)際項(xiàng)目中,我們常常需要在請(qǐng)求前后進(jìn)行一些操作,比如:參數(shù)解密/返回結(jié)果加密,打印請(qǐng)求參數(shù)和返回結(jié)果的日志等。這些與業(yè)務(wù)無(wú)關(guān)的東西,我們不希望寫(xiě)在controller方法中,造成代碼重復(fù)可讀性變差。這里,我們講講使用@ControllerAdvice和RequestBodyAdvice、ResponseBodyAdvice來(lái)對(duì)請(qǐng)求前后進(jìn)行處理(本質(zhì)上就是AOP),來(lái)實(shí)現(xiàn)日志記錄每一個(gè)請(qǐng)求的參數(shù)和返回結(jié)果。
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模式需要添加偏移量參數(shù)iv,必須16位 * 密鑰 sessionKey,必須16位 * * 介于java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 沒(méi)有什么區(qū)別 * 要實(shí)現(xiàn)在java端用PKCS7Padding填充,需要用到bouncycastle組件來(lái)實(shí)現(xiàn) */ private String sessionKey="加解密密鑰"; // 偏移量 16位 private static String iv="偏移量"; // 算法名稱(chēng) 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位,那么就補(bǔ)足. 這個(gè)if 中的內(nèi)容很重要 keybytes = iv.getBytes(); ivByte = iv.getBytes(); Security.addProvider(new BouncyCastleProvider()); // 轉(zhuǎn)化成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("加密后的內(nèi)容:" + enc); String dec=""; // 解密方法 try { dec = aes.decrypt(enc); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("解密后的內(nèi)容:" + dec); } }
前端頁(yè)面?zhèn)鬟^(guò)來(lái)的是密文,我們需要在Controller獲取請(qǐng)求之前對(duì)密文解密然后傳給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; /** * 請(qǐng)求參數(shù) 解密操作 * * @Author: Java碎碎念 * @Date: 2019/10/24 21:31 * */ @Component //可以配置指定需要解密的包,支持多個(gè) @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開(kāi)啟功能,false關(guān)閉這個(gè)功能 return true; } //在讀取請(qǐng)求之前做處理 @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException { //獲取請(qǐng)求數(shù)據(jù) String string = ""; BufferedReader bufferedReader = null; InputStream inputStream = inputMessage.getBody(); //這個(gè)request其實(shí)就是入?yún)?nbsp;可以從這里獲取流 //入?yún)⒎旁贖ttpInputMessage里面 這個(gè)方法的返回值也是HttpInputMessage try { string=getRequestBodyStr(inputStream,bufferedReader); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException ex) { throw ex; } } } /*****************進(jìn)行解密start*******************/ String decode = null; if(HttpHelper.isEncrypted(inputMessage.getHeaders())){ try { // //解密操作 //Map<String,String> dataMap = (Map)body; //log.info("接收到原始請(qǐng)求數(shù)據(jù)={}", string); // inputData 為待加解密的數(shù)據(jù)源 //解密 decode= aes.decrypt(string); //log.info("解密后數(shù)據(jù)={}",decode); } catch (Exception e ) { log.error("加解密錯(cuò)誤:",e); throw new CustomException(KPlatResponseCode.MSG_DECRYPT_TIMEOUT,KPlatResponseCode.CD_DECRYPT_TIMEOUT); } //把數(shù)據(jù)放到我們封裝的對(duì)象中 }else{ decode = string; } // log.info("接收到請(qǐng)求數(shù)據(jù)={}", decode); // log.info("接口請(qǐng)求地址{}",((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; } //自己寫(xiě)的方法,不是接口的方法,處理密文 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; } }
將返給前端的響應(yīng)加密,保證數(shù)據(jù)的安全性
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; /** * 請(qǐng)求參數(shù) 加密操作 * * @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("接口請(qǐng)求地址{}",serverHttpRequest.getURI()); //日志過(guò)濾 //retObj=infofilter.getInfoFilter(returnType,obj); if(HttpHelper.isEncrypted(serverHttpRequest.getHeaders())) { try { //添加encry header,告訴前端數(shù)據(jù)已加密 //serverHttpResponse.getHeaders().add("infoe", "e=a"); //獲取請(qǐng)求數(shù)據(jù) String srcData = JSON.toJSONString(obj); //加密 returnStr = aes.encrypt(srcData).replace("\r\n", ""); //log.info("原始數(shù)據(jù)={},加密后數(shù)據(jù)={}", obj, returnStr); return returnStr; } catch (Exception e) { log.error("異常!", e); } } log.info("原始數(shù)據(jù)={}",JSON.toJSONString(obj)); return obj; } }
到此,關(guān)于“怎么用注解+RequestBodyAdvice實(shí)現(xiàn)http請(qǐng)求內(nèi)容加解密方式”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。