使用SpringBoot怎么實現一個接口數據的加解密功能?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
創新互聯公司自成立以來,一直致力于為企業提供從網站策劃、網站設計、成都做網站、成都網站建設、電子商務、網站推廣、網站優化到為企業提供個性化軟件開發等基于互聯網的全面整合營銷服務。公司擁有豐富的網站建設和互聯網應用系統開發管理經驗、成熟的應用系統解決方案、優秀的網站開發工程師團隊及專業的網站設計師團隊。
對接口的加密解密操作主要有下面兩種方式:
自定義消息轉換器
優勢:僅需實現接口,配置簡單。
劣勢:僅能對同一類型的MediaType進行加解密操作,不靈活。
使用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); @Nullable Object 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); @Nullable T 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 { @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return true; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException { return inputMessage; } @Override public 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; } @Override public 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> { @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) { //通過 ServerHttpRequest的實現類ServletServerHttpRequest 獲得HttpServletRequest ServletServerHttpRequest 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怎么實現一個接口數據的加解密功能問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創新互聯行業資訊頻道了解更多相關知識。
文章題目:使用SpringBoot怎么實現一個接口數據的加解密功能
URL分享:http://m.newbst.com/article26/jichcg.html
成都網站建設公司_創新互聯,為您提供微信公眾號、軟件開發、電子商務、網站策劃、響應式網站、ChatGPT
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯