自定义消息转换器对请求报文的统一解密处理

发布时间 2023-12-23 14:40:05作者: 话祥

1.自定义拦截器Interceptor

package com.example.springbootdemo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class CustomInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        return true;
    }

}

2.自定义sm4的加解密工具

package com.example.springbootdemo.util;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootdemo.config.CommonLocalDateTimeSerializer;
import com.example.springbootdemo.requet.CommonRequest;
import com.example.springbootdemo.requet.LoginReq;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.time.LocalDateTime;

public class SM4Util {

    private static final String key = "w5mqd1mv318stg2w";

    public String getEncryptBase64(String jsonStr){
        SM4 sm41 = SmUtil.sm4(key.getBytes());
        String encryptHex = sm41.encryptHex(jsonStr);
        return encryptHex;
    }

    public String getDecryptStr(String EncryptBase64){
        SM4 sm41 = SmUtil.sm4(key.getBytes());
        String sourceDate = sm41.decryptStr(EncryptBase64);
        return sourceDate;
    }


    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        LoginReq loginReq = new LoginReq();
        loginReq.setUserName("zhangsan");
        loginReq.setPassword("password123");

        String s = objectMapper.writeValueAsString(loginReq);
        System.out.println(s);
        System.out.println(JSONUtil.toBean(s, LoginReq.class));
        String jsonStr = JSONUtil.toJsonStr(loginReq);
        String jsonString = JSONObject.toJSONString(loginReq);
        System.out.println(objectMapper.convertValue(jsonString, LoginReq.class));
        System.out.println(s);

    }

}

3.自定义JSON序列化配置

package com.example.springbootdemo.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class CommonLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    public static final String FULL_YMD_HMS_PATTERN = "yyyy-MM-dd HH:mm:ss";

    public static final String ZONE_ID = "Asia/Shanghai";

    @Override
    public void serialize(LocalDateTime dateTime, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(FULL_YMD_HMS_PATTERN).withZone(ZoneId.of(ZONE_ID));
        String format = dateTime.format(dateTimeFormatter);
        // dateTimeFormatter.format(dateTime);
        gen.writeString(format);
    }
}
    @Bean
    public ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 自定义LocalDateTime类型数据的json序列化方式
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new CommonLocalDateTimeSerializer());

        return objectMapper;
    }

4.自定义消息转换器

package com.example.springbootdemo.config;

import cn.hutool.json.JSONUtil;
import com.example.springbootdemo.requet.CommonRequest;
import com.example.springbootdemo.util.SM4Util;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

@Slf4j
public class CustomHttpMessageConverter extends AbstractHttpMessageConverter {

    private ObjectMapper objectMapper;

    private SM4Util sm4Util;

    private ThreadLocal<CommonRequest> threadLocal = new ThreadLocal<>();

    public CustomHttpMessageConverter(ObjectMapper objectMapper, SM4Util sm4Util) {
        this.objectMapper = objectMapper;
        this.sm4Util = sm4Util;
    }

    @Override
    protected boolean supports(Class clazz) {
        return false;
    }

    /**
     *是否使用readInternal方法处理请求报文
     * @return
     */
    @Override
    public boolean canRead(Class clazz, MediaType mediaType) {
        return true;
    }

    @Override
    protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        InputStream body = inputMessage.getBody();
        String reqBody = IOUtils.toString(body, StandardCharsets.US_ASCII);
        log.info("通用请求CommonRequest密文:{}", reqBody);
        CommonRequest request = this.objectMapper.reader().forType(CommonRequest.class).readValue(reqBody);
        String decryptStr = sm4Util.getDecryptStr(request.getReqJsonData());
        log.info("通用请求CommonRequest解密报文:{}", decryptStr);

        request.setReqJsonData(decryptStr);
        threadLocal.set(request);

        return this.objectMapper.reader().forType(clazz).readValue(decryptStr);
    }

    /**
     *是否使用writeInternal方法处理响应报文
     * @return
     */
    @Override
    protected boolean canWrite(MediaType mediaType) {
        // 对某些请求使用或者不使用消息转换器
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        request.getHeader("key1");
        request.getRequestURI();
        return false;
    }

    @Override
    protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

    }

}

5.定义WebMvcConfigurer

package com.example.springbootdemo.config;

import com.example.springbootdemo.util.SM4Util;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

@Configuration
@Slf4j
@ConditionalOnProperty(prefix = "webMvc", value = { "enable" }, havingValue = "true")
public class WebConfig implements WebMvcConfigurer {

    public CustomInterceptor customInterceptor(){
        return new CustomInterceptor();
    }

    @Bean
    public ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 自定义LocalDateTime类型数据的json序列化方式
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new CommonLocalDateTimeSerializer());

        return objectMapper;
    }

    @Bean
    public SM4Util sm4Util(){
        return new SM4Util();
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters){
        CustomHttpMessageConverter converter = new CustomHttpMessageConverter(objectMapper(), sm4Util());
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
        converters.add(0, converter);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customInterceptor()).addPathPatterns("/**");

    }


}

测试验证: