@ControllerAdvice 结合 RequestBodyAdviceAdapter 和 ResponseBodyAdvice 实现对请求的加密解密
-
@ControllerAdvice 是对controller 的增强。 结合 RequestBodyAdviceAdapter 和 ResponseBodyAdvice 实现对请求的加密解密, 可以实现类似filter 的功能, 对请求进行前置处理和后置处理。
-
应用场景
- 加密解密
-
示例
-
请求controller
-
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/test") public interface TestActionService { @PostMapping(value = "/testExchange") @RequestDecode(method = EncryptEnum.AES) @ResponseEncode(method = EncryptEnum.AES) TestVO testExchange(@RequestBody TestVO request, @RequestHeader(value = "channelCode") String channelCode); }
-
-
加密 beforeBodyRead前置处理
-
import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter; import javax.annotation.Resource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * 解密请求参数 * */ @Slf4j @Configuration // assignableTypes 指定对那个controller 类增强 @ControllerAdvice(assignableTypes = {TestActionService.class}) public class DecodeRequestBodyAdvice extends RequestBodyAdviceAdapter { @Resource private EncryptConfig encryptConfig; /** * 判断是否支持解密 * @param methodParameter * @param targetType * @param converterType * @return */ @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return methodParameter.getMethodAnnotation(RequestDecode.class) != null && methodParameter.getParameterAnnotation(RequestBody.class) != null; } /** * 在参数请求处理解密 * @param request * @param parameter * @param targetType * @param converterType * @return * @throws IOException */ @Override public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { RequestDecode requestDecode = parameter.getMethodAnnotation(RequestDecode.class); if (requestDecode == null) { return request;//controller方法不要求加解密 } EncryptEnum method = requestDecode.method(); //这里灵活的可以支持到多种加解密方式 switch (method) { case NULL: break; case AES: { InputStream is = request.getBody(); /*ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer(); int ret = -1; int len = 0; while((ret = is.read()) > 0) { buf.writeByte(ret); len ++; } String body = buf.toString(0, len, Charset.defaultCharset()); buf.release();*/ ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) != -1) { result.write(buffer, 0, length); } String body = result.toString(StandardCharsets.UTF_8.name()); String temp = null; try { //解密处理 String key = encryptConfig.getKeyMaps().get(comCode); temp = AesEncryptUtils.decrypt(body,key); log.info("解密完成: {}", temp); return new DecodedHttpInputMessage(request.getHeaders(), new ByteArrayInputStream(temp.getBytes("UTF-8"))); } catch (Exception e) { log.error("解密失败 {} 待解密密文: {}",comCode, body, e); throw new RequestException("-1","解密失败!"); } } } return request; } static class DecodedHttpInputMessage implements HttpInputMessage { HttpHeaders headers; InputStream body; public DecodedHttpInputMessage(HttpHeaders headers, InputStream body) { this.headers = headers; this.body = body; } @Override public InputStream getBody() throws IOException { return body; } @Override public HttpHeaders getHeaders() { return headers; } } }
-
-
解密
-
import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; 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 javax.annotation.Resource; /** * 响应请求加密 * */ @Slf4j @Component @ControllerAdvice(assignableTypes = {TestActionService.class}) public class EncodeResponseBodyAdvic implements ResponseBodyAdvice { @Resource private EncryptConfig encryptConfig; @Override public boolean supports(MethodParameter returnType, Class converterType) { return returnType.getMethodAnnotation(ResponseEncode.class) != null; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { ResponseEncode responseEncode = returnType.getMethodAnnotation(ResponseEncode.class); EncryptEnum method = responseEncode.method(); switch (method) { case NULL: break; case AES: { String temp = JSON.toJSONString(body); log.info("待加密数据: {}", temp); try{ String channelCode = request.getHeaders().getFirst("channelCode"); String key =encryptConfig.getKeyMaps().get(channelCode); String encryptBody = AesEncryptUtils.encrypt(temp, key); log.debug("加密完成: {}", encryptBody); response.getHeaders().set("Content-Type", "application/json;charset=UTF-8"); return encryptBody; }catch (Exception e){ log.error("加密异常:",e); throw new RequestException("-1","加密异常!"); } } } return body; } }
-
-