使用自定义HttpMessageConverter对返回内容进行加密

今天上午技术群里的一个人问”如何在Spring MVC中统一对返回的Json进行加密?”。

大部分人的第一反应是通过Spring拦截器(Interceptor)中的postHandler方法处理。实际这是行不通的,因为当程序运行到该方法,是在返回数据之后,渲染页面之前,所以这时候Response中的输出流已经关闭了,自然无法在对返回数据进行处理。

其实这个问题用几行代码就可以搞定,因为Spring提供了非常丰富的扩展支持,无论是之前提到的Interceptor和MethodArgumentResolver,还是接下来要提到的HttpMessageConverter。

在Spring MVC的 Controller层经常会用到@RequestBody和@ResponseBody,通过这两个注解,可以在Controller中直接使用Java对象作为请求参数和返回内容,而完成这之间转换作用的便是HttpMessageConverter。

HttpMessageConverter接口提供了5个方法:

  • canRead:判断该转换器是否能将请求内容转换成Java对象
  • canWrite:判断该转换器是否可以将Java对象转换成返回内容
  • getSupportedMediaTypes:获得该转换器支持的MediaType类型
  • read:读取请求内容并转换成Java对象
  • write:将Java对象转换后写入返回内容

其中read和write方法的参数分别有有HttpInputMessage和HttpOutputMessage对象,这两个对象分别代表着一次Http通讯中的请求和响应部分,可以通过getBody方法获得对应的输入流和输出流。

这里通过实现该接口自定义一个Json转换器作为示例:

class CustomJsonHttpMessageConverter implements HttpMessageConverter {

    //Jackson的Json映射类
    private ObjectMapper mapper = new ObjectMapper();

    //该转换器的支持类型:application/json
    private List supportedMediaTypes = Arrays.asList(MediaType.APPLICATION_JSON);

    /**
     * 判断转换器是否可以将输入内容转换成Java类型
     * @param clazz     需要转换的Java类型
     * @param mediaType 该请求的MediaType
     * @return
     */
    @Override
    public boolean canRead(Class clazz, MediaType mediaType) {
        if (mediaType == null) {
            return true;
        }
        for (MediaType supportedMediaType : getSupportedMediaTypes()) {
            if (supportedMediaType.includes(mediaType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断转换器是否可以将Java类型转换成指定输出内容
     * @param clazz     需要转换的Java类型
     * @param mediaType 该请求的MediaType
     * @return
     */
    @Override
    public boolean canWrite(Class clazz, MediaType mediaType) {
        if (mediaType == null || MediaType.ALL.equals(mediaType)) {
            return true;
        }
        for (MediaType supportedMediaType : getSupportedMediaTypes()) {
            if (supportedMediaType.includes(mediaType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获得该转换器支持的MediaType
     * @return
     */
    @Override
    public List getSupportedMediaTypes() {
        return supportedMediaTypes;
    }

    /**
     * 读取请求内容,将其中的Json转换成Java对象
     * @param clazz         需要转换的Java类型
     * @param inputMessage  请求对象
     * @return
     * @throws IOException
     * @throws HttpMessageNotReadableException
     */
    @Override
    public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return mapper.readValue(inputMessage.getBody(), clazz);
    }

    /**
     * 将Java对象转换成Json返回内容
     * @param o             需要转换的对象
     * @param contentType   返回类型
     * @param outputMessage 回执对象
     * @throws IOException
     * @throws HttpMessageNotWritableException
     */
    @Override
    public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        mapper.writeValue(outputMessage.getBody(), o);
    }
}

当前Spring中已经默认提供了相当多的转换器,分别有:

名称作用读支持MediaType写支持MediaType
ByteArrayHttpMessageConverter数据与字节数组的相互转换*/*application/octet-stream
StringHttpMessageConverter数据与String类型的相互转换text/*text/plain
FormHttpMessageConverter表单与MultiValueMap的相互转换application/x-www-form-urlencodedapplication/x-www-form-urlencoded
SourceHttpMessageConverter数据与javax.xml.transform.Source的相互转换text/xml和application/xmltext/xml和application/xml
MarshallingHttpMessageConverter使用Spring的Marshaller/Unmarshaller转换XML数据text/xml和application/xmltext/xml和application/xml
MappingJackson2HttpMessageConverter使用Jackson的ObjectMapper转换Json数据application/jsonapplication/json
MappingJackson2XmlHttpMessageConverter使用Jackson的XmlMapper转换XML数据application/xmlapplication/xml
BufferedImageHttpMessageConverter数据与java.awt.image.BufferedImage的相互转换Java I/O API支持的所有类型Java I/O API支持的所有类型

回到最开始所提的需求,既然要对返回的Json内容进行加密,肯定是对MappingJackson2HttpMessageConverter进行改造,并且只需要重写write方法。

从MappingJackson2HttpMessageConverter的父类AbstractHttpMessageConverter中的write方法可以看出,该方法通过writeInternal方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    return new MappingJackson2HttpMessageConverter() {
        //重写writeInternal方法,在返回内容前首先进行加密
        @Override
        protected void writeInternal(Object object,
                                     HttpOutputMessage outputMessage) throws IOException,
                HttpMessageNotWritableException {
            //使用Jackson的ObjectMapper将Java对象转换成Json String
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(object);
            LOGGER.error(json);
            //加密
            String result = json + "加密了!";
            LOGGER.error(result);
            //输出
            outputMessage.getBody().write(result.getBytes());
        }
    };
}

在这之后还需要将这个自定义的转换器配置到Spring中,这里通过重写WebMvcConfigurer中的configureMessageConverters方法添加自定义转换器:

//添加自定义转换器
@Override
public void configureMessageConverters(List> converters) {
    converters.add(mappingJackson2HttpMessageConverter());
    super.configureMessageConverters(converters);
}

测试一下:
这里写图片描述

如此便简单的完成了对返回内容进行加密的功能。
转载自:http://www.scienjus.com/custom-http-message-converter/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值