日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

SpringBoot中如何灵活的实现接口数据的加解密功能?

發布時間:2025/3/16 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot中如何灵活的实现接口数据的加解密功能? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據是企業的第四張名片,企業級開發中少不了數據的加密傳輸,所以本文介紹下SpringBoot中接口數據加密、解密的方式。

本文目錄

一、加密方案介紹二、實現原理三、實戰四、測試五、踩到的坑

一、加密方案介紹

對接口的加密解密操作主要有下面兩種方式:

  • 自定義消息轉換器

  • 優勢:僅需實現接口,配置簡單。

  • 使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice

  • 優勢:可以按照請求的Referrer、Header或url進行判斷,按照特定需要進行加密解密。

    比如在一個項目升級的時候,新開發功能的接口需要加解密,老功能模塊走之前的邏輯不加密,這時候就只能選擇上面的第二種方式了,下面主要介紹下第二種方式加密、解密的過程。

    二、實現原理

    RequestBodyAdvice可以理解為在@RequestBody之前需要進行的 操作,ResponseBodyAdvice可以理解為在@ResponseBody之后進行的操作,所以當接口需要加解密時,在使用@RequestBody接收前臺參數之前可以先在RequestBodyAdvice的實現類中進行參數的解密,當操作結束需要返回數據時,可以在@ResponseBody之后進入ResponseBodyAdvice的實現類中進行參數的加密。

    RequestBodyAdvice處理請求的過程:

    RequestBodyAdvice源碼如下:

    ?public?interface?RequestBodyAdvice?{boolean?supports(MethodParameter?methodParameter,?Type?targetType,Class<??extends?HttpMessageConverter<?>>?converterType);HttpInputMessage?beforeBodyRead(HttpInputMessage?inputMessage,?MethodParameter?parameter,Type?targetType,?Class<??extends?HttpMessageConverter<?>>?converterType)?throws?IOException;Object?afterBodyRead(Object?body,?HttpInputMessage?inputMessage,?MethodParameter?parameter,Type?targetType,?Class<??extends?HttpMessageConverter<?>>?converterType);@NullableObject?handleEmptyBody(@Nullable?Object?body,?HttpInputMessage?inputMessage,?MethodParameter?parameter,Type?targetType,?Class<??extends?HttpMessageConverter<?>>?converterType);}

    調用RequestBodyAdvice實現類的部分代碼如下:

    ?protected?<T>?Object?readWithMessageConverters(HttpInputMessage?inputMessage,?MethodParameter?parameter,Type?targetType)?throws?IOException,?HttpMediaTypeNotSupportedException,?HttpMessageNotReadableException?{MediaType?contentType;boolean?noContentType?=?false;try?{contentType?=?inputMessage.getHeaders().getContentType();}catch?(InvalidMediaTypeException?ex)?{throw?new?HttpMediaTypeNotSupportedException(ex.getMessage());}if?(contentType?==?null)?{noContentType?=?true;contentType?=?MediaType.APPLICATION_OCTET_STREAM;}Class<?>?contextClass?=?parameter.getContainingClass();Class<T>?targetClass?=?(targetType?instanceof?Class???(Class<T>)?targetType?:?null);if?(targetClass?==?null)?{ResolvableType?resolvableType?=?ResolvableType.forMethodParameter(parameter);targetClass?=?(Class<T>)?resolvableType.resolve();}HttpMethod?httpMethod?=?(inputMessage?instanceof?HttpRequest???((HttpRequest)?inputMessage).getMethod()?:?null);Object?body?=?NO_VALUE;EmptyBodyCheckingHttpInputMessage?message;try?{message?=?new?EmptyBodyCheckingHttpInputMessage(inputMessage);for?(HttpMessageConverter<?>?converter?:?this.messageConverters)?{Class<HttpMessageConverter<?>>?converterType?=?(Class<HttpMessageConverter<?>>)?converter.getClass();GenericHttpMessageConverter<?>?genericConverter?=(converter?instanceof?GenericHttpMessageConverter???(GenericHttpMessageConverter<?>)?converter?:?null);if?(genericConverter?!=?null???genericConverter.canRead(targetType,?contextClass,?contentType)?:(targetClass?!=?null?&&?converter.canRead(targetClass,?contentType)))?{if?(logger.isDebugEnabled())?{logger.debug("Read?["?+?targetType?+?"]?as?\""?+?contentType?+?"\"?with?["?+?converter?+?"]");}if?(message.hasBody())?{HttpInputMessage?msgToUse?=getAdvice().beforeBodyRead(message,?parameter,?targetType,?converterType);body?=?(genericConverter?!=?null???genericConverter.read(targetType,?contextClass,?msgToUse)?:((HttpMessageConverter<T>)?converter).read(targetClass,?msgToUse));body?=?getAdvice().afterBodyRead(body,?msgToUse,?parameter,?targetType,?converterType);}else?{body?=?getAdvice().handleEmptyBody(null,?message,?parameter,?targetType,?converterType);}break;}}}catch?(IOException?ex)?{throw?new?HttpMessageNotReadableException("I/O?error?while?reading?input?message",?ex);}if?(body?==?NO_VALUE)?{if?(httpMethod?==?null?||?!SUPPORTED_METHODS.contains(httpMethod)?||(noContentType?&&?!message.hasBody()))?{return?null;}throw?new?HttpMediaTypeNotSupportedException(contentType,?this.allSupportedMediaTypes);}return?body;}

    從上面源碼可以到當converter.canRead()和message.hasBody()都為true的時候,會調用beforeBodyRead()和afterBodyRead()方法,所以我們在實現類的afterBodyRead()中添加解密代碼即可。

    ResponseBodyAdvice處理響應的過程:

    ResponseBodyAdvice源碼如下:

    public?interface?ResponseBodyAdvice<T>?{boolean?supports(MethodParameter?returnType,?Class<??extends?HttpMessageConverter<?>>?converterType);@NullableT?beforeBodyWrite(@Nullable?T?body,?MethodParameter?returnType,?MediaType?selectedContentType,Class<??extends?HttpMessageConverter<?>>?selectedConverterType,ServerHttpRequest?request,?ServerHttpResponse?response);}

    調用ResponseBodyAdvice實現類的部分代碼如下:

    if?(selectedMediaType?!=?null)?{selectedMediaType?=?selectedMediaType.removeQualityValue();for?(HttpMessageConverter<?>?converter?:?this.messageConverters)?{GenericHttpMessageConverter?genericConverter?=(converter?instanceof?GenericHttpMessageConverter???(GenericHttpMessageConverter<?>)?converter?:?null);if?(genericConverter?!=?null??((GenericHttpMessageConverter)?converter).canWrite(declaredType,?valueType,?selectedMediaType)?:converter.canWrite(valueType,?selectedMediaType))?{outputValue?=?(T)?getAdvice().beforeBodyWrite(outputValue,?returnType,?selectedMediaType,(Class<??extends?HttpMessageConverter<?>>)?converter.getClass(),inputMessage,?outputMessage);if?(outputValue?!=?null)?{addContentDispositionHeader(inputMessage,?outputMessage);if?(genericConverter?!=?null)?{genericConverter.write(outputValue,?declaredType,?selectedMediaType,?outputMessage);}else?{((HttpMessageConverter)?converter).write(outputValue,?selectedMediaType,?outputMessage);}if?(logger.isDebugEnabled())?{logger.debug("Written?["?+?outputValue?+?"]?as?\""?+?selectedMediaType?+"\"?using?["?+?converter?+?"]");}}return;}}}

    從上面源碼可以到當converter.canWrite()為true的時候,會調用beforeBodyWrite()方法,所以我們在實現類的beforeBodyWrite()中添加解密代碼即可。

    三、實戰

    新建一個spring boot項目spring-boot-encry,按照下面步驟操作。

  • pom.xml中引入jar

  • ??<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.60</version></dependency></dependencies>
  • 請求參數解密攔截類

  • DecryptRequestBodyAdvice代碼如下:

    /***?請求參數?解密操作**?@Author:?Java碎碎念*?@Date:?2019/10/24?21:31**/ @Component @ControllerAdvice(basePackages?=?"com.example.springbootencry.controller") @Slf4j public?class?DecryptRequestBodyAdvice?implements?RequestBodyAdvice?{@Overridepublic?boolean?supports(MethodParameter?methodParameter,?Type?targetType,?Class<??extends?HttpMessageConverter<?>>?converterType)?{return?true;}@Overridepublic?HttpInputMessage?beforeBodyRead(HttpInputMessage?inputMessage,?MethodParameter?methodParameter,?Type?targetType,?Class<??extends?HttpMessageConverter<?>>?selectedConverterType)?throws?IOException?{return?inputMessage;}@Overridepublic?Object?afterBodyRead(Object?body,?HttpInputMessage?inputMessage,?MethodParameter?parameter,?Type?targetType,?Class<??extends?HttpMessageConverter<?>>?converterType)?{String?dealData?=?null;try?{//解密操作Map<String,String>?dataMap?=?(Map)body;String?srcData?=?dataMap.get("data");dealData?=?DesUtil.decrypt(srcData);}?catch?(Exception?e)?{log.error("異常!",?e);}return?dealData;}@Overridepublic?Object?handleEmptyBody(@Nullable?Object?var1,?HttpInputMessage?var2,?MethodParameter?var3,?Type?var4,?Class<??extends?HttpMessageConverter<?>>?var5)?{log.info("3333");return?var1;}}
  • 響應參數加密攔截類

  • EncryResponseBodyAdvice代碼如下:

    /***?請求參數?解密操作**?@Author:?Java碎碎念*?@Date:?2019/10/24?21:31**/ @Component @ControllerAdvice(basePackages?=?"com.example.springbootencry.controller") @Slf4j public?class?EncryResponseBodyAdvice?implements?ResponseBodyAdvice<Object>?{@Overridepublic?boolean?supports(MethodParameter?returnType,?Class<??extends?HttpMessageConverter<?>>?converterType)?{return?true;}@Overridepublic?Object?beforeBodyWrite(Object?obj,?MethodParameter?returnType,?MediaType?selectedContentType,Class<??extends?HttpMessageConverter<?>>?selectedConverterType,?ServerHttpRequest?serverHttpRequest,ServerHttpResponse?serverHttpResponse)?{//通過?ServerHttpRequest的實現類ServletServerHttpRequest?獲得HttpServletRequestServletServerHttpRequest?sshr?=?(ServletServerHttpRequest)?serverHttpRequest;//此處獲取到request?是為了取到在攔截器里面設置的一個對象?是我項目需要,可以忽略HttpServletRequest?request?=?sshr.getServletRequest();String?returnStr?=?"";try?{//添加encry?header,告訴前端數據已加密serverHttpResponse.getHeaders().add("encry",?"true");String?srcData?=?JSON.toJSONString(obj);//加密returnStr?=?DesUtil.encrypt(srcData);log.info("接口={},原始數據={},加密后數據={}",?request.getRequestURI(),?srcData,?returnStr);}?catch?(Exception?e)?{log.error("異常!",?e);}return?returnStr;}
  • 新建controller類

  • TestController代碼如下:

    /***?@Author:?Java碎碎念*?@Date:?2019/10/24?21:40*/ @RestController public?class?TestController?{Logger?log?=?LoggerFactory.getLogger(getClass());/***?響應數據?加密*/@RequestMapping(value?=?"/sendResponseEncryData")public?Result?sendResponseEncryData()?{Result?result?=?Result.createResult().setSuccess(true);result.setDataValue("name",?"Java碎碎念");result.setDataValue("encry",?true);return?result;}/***?獲取?解密后的?請求參數*/@RequestMapping(value?=?"/getRequestData")public?Result?getRequestData(@RequestBody?Object?object)?{log.info("controller接收的參數object={}",?object.toString());Result?result?=?Result.createResult().setSuccess(true);return?result;} }
  • 其他類在源碼中,后面有github地址

  • 四、測試

  • 訪問響應數據加密接口

  • 使用postman發請求http://localhost:8888/sendResponseEncryData,可以看到返回數據已加密,請求截圖如下:

    響應數據加密截圖

    后臺也打印相關的日志,內容如下:

    接口=/sendResponseEncryData原始數據={"data":{"encry":true,"name":"Java碎碎念"},"success":true}加密后數據=vJc26g3SQRU9gAJdG7rhnAx6Ky/IhgioAgdwi6aLMMtyynAB4nEbMxvDsKEPNIa5bQaT7ZAImAL7 3VeicCuSTA==
  • 訪問請求數據解密接口

  • 使用postman發請求http://localhost:8888/getRequestData,可以看到請求數據已解密,請求截圖如下:

    請求數據解密截圖

    后臺也打印相關的日志,內容如下:

    接收到原始請求數據={"data":"VwLvdE8N6FuSxn/jRrJavATopaBA3M1QEN+9bkuf2jPwC1eSofgahQ=="}解密后數據={"name":"Java碎碎念","des":"請求參數"}

    五、踩到的坑

  • 測試解密請求參數時候,請求體一定要有數據,否則不會調用實現類觸發解密操作。

  • 到此SpringBoot中如何靈活的實現接口數據的加解密功能的功能已經全部實現,有問題歡迎留言溝通哦!

    完整源碼地址: https://github.com/suisui2019/springboot-study

    有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的SpringBoot中如何灵活的实现接口数据的加解密功能?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。