快捷搜索:  汽车  科技

response功能详解(使用ResponseBodyAdvice统一对响应的数据进行处理)

response功能详解(使用ResponseBodyAdvice统一对响应的数据进行处理)import java.nio.charset.StandardCharsets; import java.util.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.GsonHttpM

开发遇到一个需求,就是对响应给APP的json数据进行加密处理。知道 ResponseBodyAdvice 可以干这事儿,但是一直没用过。所以,这次专门学习了一下

ResponseBodyAdvice

public interface ResponseBodyAdvice<T> { /** * @param returnType 响应的数据类型 * @param converterType 最终将会使用的消息转换器 * @return 返回bool,表示是否要在响应之前执行“beforeBodyWrite” 方法 */ boolean supports(MethodParameter returnType Class<? extends HttpMessageConverter<?>> converterType); /** * @param body 响应的数据,也就是响应体 * @param returnType 响应的数据类型 * @param selectedContentType 响应的ContentType * @param selectedConverterType 最终将会使用的消息转换器 * @param request * @param response * @return 被修改后的响应体,可以为null,表示没有任何响应 */ @Nullable T beforeBodyWrite(@Nullable T body MethodParameter returnType MediaType selectedContentType Class<? extends HttpMessageConverter<?>> selectedConverterType ServerHttpRequest request ServerHttpResponse response); }

蛮简单的东西,通过泛型,指定需要被“拦截”的响应体对象类型。

该接口的实现会在 Controller 方法返回数据,并且匹配到了 HttpMessageConverter 之后。HttpMessageConverter 进行序列化之前执行。可以通过覆写 beforeBodyWrite 来统一的对响应体进行修改。

注意,需要通过标识 @ControllerAdvice 注解激活。

Demo

EncodeResponseBodyAdvice

import java.nio.charset.StandardCharsets; import java.util.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import com.google.gson.Gson; import io.springboot.common.Message; import io.springboot.common.exception.ServiceException; import io.springboot.common.utils.AESUtils; /** * * 对响应体进行加密 * @author Administrator * */ @ControllerAdvice public class EncodeResponseBodyAdvice implements ResponseBodyAdvice<Message<Object>> { static final Logger LOGGER = LoggerFactory.getLogger(EncodeResponseBodyAdvice.class); @Autowired Gson gson; /** * 128位AES密钥 */ public static final byte[] KEY = "9f5d54580044d478".getBytes(); @Override public boolean supports(MethodParameter returnType Class<? extends HttpMessageConverter<?>> converterType) { /** * 返回对象必须是 Message 并且使用了 GsonHttpMessageConverter */ return GsonHttpMessageConverter.class.isAssignableFrom(converterType); } @Override public Message<Object> beforeBodyWrite(Message<Object> body MethodParameter returnType MediaType selectedContentType Class<? extends HttpMessageConverter<?>> selectedConverterType ServerHttpRequest request ServerHttpResponse response) { Object data = body.getData(); if (data != null) { /** * 重写data,进行加密 */ body.setData(this.encode(data)); } return body; } private String encode(Object data) { String jsonValue = gson.toJson(data); try { String cipher = Base64.getEncoder() .encodeToString(AESUtils.encrypt(jsonValue.getBytes(StandardCharsets.UTF_8) KEY)); if (LOGGER.isDebugEnabled()) { LOGGER.debug("响应体AES加密:raw={} cipher={}" jsonValue cipher); } return cipher; } catch (Exception e) { throw new ServiceException(Message.fail(Message.Code.INTERNAL_SERVER_ERROR "对数据加密异常:" e.getMessage()) e); } } }

AESUtils

import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; /** * * AES * * */ public class AESUtils { private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding"; // 获取 cipher private static Cipher getCipher(byte[] key int model) throws Exception { SecretKeySpec secretKeySpec = new SecretKeySpec(key "AES"); Cipher cipher = Cipher.getInstance(AES_ALGORITHM); cipher.init(model secretKeySpec); return cipher; } // AES加密 public static byte[] encrypt(byte[] data byte[] key) throws Exception NoSuchPaddingException IllegalBlockSizeException BadPaddingException { Cipher cipher = getCipher(key Cipher.ENCRYPT_MODE); return cipher.doFinal(data); } // AES解密 public static byte[] decrypt(byte[] data byte[] key) throws Exception NoSuchPaddingException IllegalBlockSizeException BadPaddingException { Cipher cipher = getCipher(key Cipher.DECRYPT_MODE); return cipher.doFinal(data); } }

演示

Controller

@RequestMapping("/test") @RestController public class TestController { @GetMapping public Object test () { return Message.success(Collections.singletonMap("name" "SpringBoot中文社区")); } }

加密后的内容

response功能详解(使用ResponseBodyAdvice统一对响应的数据进行处理)(1)

成功解密

response功能详解(使用ResponseBodyAdvice统一对响应的数据进行处理)(2)

image1148×468 13.9 KB

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); }

原文:https://springboot.io/t/topic/1864

猜您喜欢: