1 自定义注解 该注解放在作用再controller上

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface EncryptFilter {

    /**
     * 对入参是否解密
     * @return
     */
    boolean decryptRequest() default true;


    /**
     * 对出参是否加密
     */
    boolean encryptResponse() default true;


}

2 对请求数据进行数据处理


/**
 * <p>接口入参解密</p>
 * RequestBodyAdvice可以理解为在@RequestBody之前需要进行的 操作,<br/>
 * ResponseBodyAdvice可以理解为在@ResponseBody之后进行的操作;<br/>
 * 所以当接口需要加解密时,在使用@RequestBody接收前台参数之前可以先在RequestBodyAdvice的实现类中进行参数的解密,<br/>
 * 当操作结束需要返回数据时,可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。<br/>
 */
@RestControllerAdvice
@Slf4j
public class DecryptRequestBodyAdapter extends RequestBodyAdviceAdapter {

    /**
     * 该方法用于判断当前请求,是否要执行beforeBodyRead方法
     *
     * @param methodParameter handler方法的参数对象
     * @param targetType      handler方法的参数类型
     * @param converterType   将会使用到的Http消息转换器类类型
     * @return 返回true则会执行beforeBodyRead
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        Method method = methodParameter.getMethod();
        if (Objects.nonNull(method)) {
            EncryptFilter encryptFilter = method.getAnnotation(EncryptFilter.class);
            if (Objects.nonNull(encryptFilter)) {
                return encryptFilter.decryptRequest();
            }
        }
        return false;
    }

    /**
     * 在Http消息转换器执转换,之前执行
     *
     * @param inputMessage    客户端的请求数据
     * @param methodParameter handler方法的参数对象
     * @param targetType      handler方法的参数类型
     * @param converterType   将会使用到的Http消息转换器类类型
     * @return 返回 一个自定义的HttpInputMessage
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType,
                                           Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        // 读取加密的请求体
        InputStream body = inputMessage.getBody();
        HttpHeaders headers = inputMessage.getHeaders();
        headers.remove("Content-Length");
        String s = StreamUtil.getBodyString(body);
        log.info("解密前请求body:" + s);
        if (Strings.isNotEmpty(s)) {
            // 使用sm4解密
            String bodyDec = null;
            try {
                bodyDec = Sm4Util.decryptEcb(Constants.KEY,s);
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("解密后请求body:" + bodyDec);
            if (Strings.isNotEmpty(bodyDec)) {
                // 使用解密后的数据,构造新的读取流
                InputStream inputStream = new ByteArrayInputStream(bodyDec.getBytes(StandardCharsets.UTF_8));
                return new HttpInputMessage() {
                    @Override
                    public HttpHeaders getHeaders() {
                        return inputMessage.getHeaders();
                    }

                    @Override
                    public InputStream getBody() {
                        return inputStream;
                    }
                };
            }
        }

        return inputMessage;
    }

3 对返回结果进行数据加密 继承ResponseBodyAdvice


/**
 * <p>接口出参加密</p>
 * RequestBodyAdvice可以理解为在@RequestBody之前需要进行的 操作,<br/>
 * ResponseBodyAdvice可以理解为在@ResponseBody之后进行的操作;<br/>
 * 所以当接口需要加解密时,在使用@RequestBody接收前台参数之前可以先在RequestBodyAdvice的实现类中进行参数的解密,<br/>
 * 当操作结束需要返回数据时,可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。<br/>
 */
//@RestControllerAdvice("com.example.springbootEncrypt.controller")
// 表示com.example.springbootEncrypt.controller此包下的所有响应对象都会经过此拦截器,并对响应体加密
@RestControllerAdvice
@Slf4j
public class EncryptResponseBodyAdapter implements ResponseBodyAdvice<Object> {


    /**
     * 该方法用于判断当前请求的返回值,是否要执行beforeBodyWrite方法
     *
     * @param methodParameter handler方法的参数对象
     * @param converterType   将会使用到的Http消息转换器类类型
     * @return 返回true则会执行beforeBodyWrite
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        Method method = methodParameter.getMethod();
        if (Objects.nonNull(method)) {
            EncryptFilter encryptFilter = method.getAnnotation(EncryptFilter.class);
            if (Objects.nonNull(encryptFilter)) {
                return encryptFilter.encryptResponse();
            }
        }
        return false;
    }

    /**
     * 在Http消息转换器执转换,之前执行
     *
     * @param body               服务端的响应数据
     * @param methodParameter    handler方法的参数对象
     * @param mediaType          响应的ContentType
     * @param converterType      将会使用到的Http消息转换器类类型
     * @param serverHttpRequest  serverHttpRequest
     * @param serverHttpResponse serverHttpResponse
     * @return 返回 一个自定义的HttpInputMessage,可以为null,表示没有任何响应
     */
    @Override
    @Nullable
    public Object beforeBodyWrite(@Nullable Object body, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> converterType,
                                  ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        log.info("处理请求地址:{} 的返回值", serverHttpRequest.getURI());
        //获取请求数据
        String srcData = JSON.toJSONString(body);
        log.info("加密前响应body={}", srcData);
        if (Objects.nonNull(body)) {
            //加密
            String returnStr = null;
            try {
                returnStr = Sm4Util.encryptEcb(Constants.KEY,srcData);
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("加密后响应body:" + returnStr);

            //添加 encrypt 告诉前端数据已加密
            //serverHttpResponse.getHeaders().add("encrypt", "e=a");
            return returnStr;
        }
        return body;
    }

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐