基于自定义注解的参数解析器

背景

在工作中,常会对接一些第三方应用,通常第三方会采用Rsa+Aes加密来通信,但是每个第三方的加密方式可能不一致。此时得好好规划一下实现方案了。因此我们采用注解+Spring自带了参数解析类,实现自己的参数解析器。

原理

通过HandlerMethodArgumentResolver实现自定义参数解析

实现

步骤1:创建注解,例如创建一个解析Aes加密的注解AesJson

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AesJson {
}

步骤2:创建加密参数实体类对象

传入的JSON数据格式为{“data”:“xxxxxxxxxx”}


import lombok.Data;

@Data
public class AesBaseParams {
    private String data;
}

步骤3:新建AesArgumentResolver类实现HandlerMethodArgumentResolver


import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * aes加密算法加密的入参,通过@AesJson注解自动解析
 */
public class AesArgumentResolver implements HandlerMethodArgumentResolver {

    private static final Logger logger = LoggerFactory.getLogger(AesArgumentResolver.class);

    /**
     * 支持该注解的方法
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(AesJson.class);
    }

    /**
     * 解析aes加密入参
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest httpServletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        String para = getAllParamJson(httpServletRequest);
        AesBaseParams aesBaseParams = JSON.parseObject(para, AesBaseParams.class);
        Object obj = null;
        if(Objects.nonNull(aesBaseParams)){
            //如果是aes加密的请求参数不为空
            String data  = aesBaseParams.getData();
            if(StringUtils.isNotEmpty(data)){
                //如果不为空 ,进行解密,data为加密数据,key为aes密钥,此工具类自行获取
                String aesDecodeStr = EncryptUtils.decryptByAES(data,key);
				//.....
				//验签、RSA解密操作、AES解密操作
                //将解密后的json字符串,解析成实体对象
                obj = JSON.parseObject(aesDecodeStr,methodParameter.getParameterType());
            }
        }
		if(Objects.isNull(obj)){
			//抛出异常,解密出错
			throw new BaseException(ErrorCodesEnum.DECRYPT_ERROR);
		}
        return obj;
    }

    /**
     *
     * 获取application/json格式提交的请求参数的 json字符串
     * @param httpServletRequest
     * @return
     * @throws IOException
     */
    private String getAllParamJson(HttpServletRequest httpServletRequest) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = httpServletRequest.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        return stringBuilder.toString();
    }

}

步骤4:注入AesArgumentResolver,实现WebMvcConfigurer并重写addArgumentResolvers方法

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * 创建ArgumentResolverConfig
 */
@Configuration
public class ArgumentResolverConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers){
        argumentResolvers.add(aesArgumentResolver());
    }
    @Bean
    public AesArgumentResolver aesArgumentResolver(){
        return new AesArgumentResolver();
    }
}

步骤5:如何使用

在请求参数对象前添加AesJson注解就可以自动解析参数啦

/**
 * 推单接口
 * @param reqVo
 * @return
 */
@PostMapping("/pushOrder")
public PushOrderRspVo pushOrder(@AesJson PushOrderReqVo reqVo){
	String orderNo = reqVo.getOrderNo();
	//执行推单逻辑
}