SpringBoot实现在Request里解密参数返回

Spring Boot 在request里解密返回参数到控制器
前言

有个业务需求,一个请求来源web,一个请求来源APP,web需求验证签名,APP的参数是经过加密,所以出现了两个Controller,除了解密获取参数方式不一样,其他内容一模一样,这样不太合理,所以我决定重构。

思路:既然只是解密不一样,获取到的参数是一样的,那可以写一个过滤器,在里面就把参数解密好,然后返回

Spring Boot在请求的时候是不允许直接修改HttpServletRequest里的paramsMap参数的,但是提供了一个HttpServletRequestWrapper类,继承这个类重写两个方法就可以了。

代码块

重写HttpServletRequestWrapper

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

public class ParameterRequest extends HttpServletRequestWrapper {
    private Map<String, String[]> params = new HashMap<>(16);

    public ParameterRequest(HttpServletRequest request) throws IOException {
        super(request);
        this.params.putAll(request.getParameterMap());
    }

    /**
     * 重载一个构造方法
     *
     * @param request
     * @param extendParams
     */
    public ParameterRequest(HttpServletRequest request, Map<String, String[]> extendParams) throws IOException {
        this(request);
        addAllParameters(extendParams);
    }


    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }

    public void addAllParameters(Map<String, String[]> otherParams) {
        for (Map.Entry<String, String[]> entry : otherParams.entrySet()) {
            addParameter(entry.getKey(), entry.getValue());
        }
    }


    public void addParameter(String name, Object value) {
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }

}

思路是重写自定义一个Map存入参数,将解密后需要的参数放入,然后在过滤器中执行这个新的request

过滤器

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class WebParamFilter implements Filter {


    private static final String OPTIONS = "OPTIONS";
    @Value("${jwt.info.urlPatterns}")
    private List<String> urlPatterns;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        log.info("开始过滤器===============");
        if (!isFilter(request)) {
            writerError(response, RetEnum.RET_TOKEN_ERROR);
            return;
        }
        // 从请求头从获取请求类型,1是WEB,2是APP
        String requestType = request.getHeader("requestType");
        if (StringUtils.isEmpty(requestType)) {
            writerError(response, RetEnum.RET_NOT_HEADER_ERROR);
            return;
        }
        Map<String, String[]> paramsMap = new HashMap<>();
        if ("1".equals(requestType)) {
        	// 验证签名,签名错误直接返回
            if (!compareSign(request)) {
                writerError(response, "签名错误", 500);
                return;
            }
            // 将请求的参数从request中取出,转换成JSON,放入自定义的Map中带给控制器
            paramsMap.put("params", new String[]{JSONUtil.getJSONParam(request).toJSONString()});
            ParameterRequest req = new ParameterRequest(request, paramsMap);
            filterChain.doFilter(req, response);
        } else if ("2".equals(requestType)) {
        	// APP请求方式比较特殊,所以要从requestBody里读出JSON加密数据
            String bodyStr = RequestBodyUtil.read(request.getReader());
            // 然后再解密,拿到真正的参数转换成JSON,放入自定义的Map中带给控制器
            JSONObject jsonParam = getJsonParam(bodyStr);
            paramsMap.put("params", new String[]{jsonParam.toJSONString()});
            ParameterRequest req = new ParameterRequest(request, paramsMap);
            filterChain.doFilter(req, response);
        } else {
            writerError(response, "无效的请求来源", 500);
        }
    }

    @Override
    public void destroy() {

    }

    /**
     * 筛选
     *
     * @param request
     * @return
     */
    private boolean isFilter(HttpServletRequest request) {
        if (OPTIONS.equals(request.getMethod())) {
            return true;
        }
        if (isInclude(request)) {
            //如果是属于排除的URL,比如登录,注册,验证码等URL,则直接通行
            log.info("直接通过");
            return true;
        }
        return tokenCheck(request);
    }

    /**
     * 排除不需要过滤的URL
     *
     * @param request
     * @return
     */
    private boolean isInclude(HttpServletRequest request) {
        String url = request.getRequestURI().substring(request.getContextPath().length());
        log.info("请求url:{}", url);
        for (String patternUrl : urlPatterns) {
            Pattern p = Pattern.compile(patternUrl);
            Matcher m = p.matcher(url);
            if (m.find()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 效验token是否有效
     *
     * @param request
     * @return
     */
    private boolean tokenCheck(HttpServletRequest request) {
        String authToken = request.getHeader("accessToken");
        log.info("请求头中令牌token:{}", authToken);
        // ...业务代码
        return false;
    }

    /**
     * 错误写出
     *
     * @param response
     * @param retEnum
     * @throws IOException
     */
    private void writerError(HttpServletResponse response, String msg, int code) throws IOException {
        //验证不通过
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        //将验证不通过的错误返回
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> resultMap = new HashMap<>(3);
        resultMap.put("code", code);
        resultMap.put("msg", msg);
        resultMap.put("data", null);
        response.getWriter().write(mapper.writeValueAsString(resultMap));
    }

    /**
     * web效验签名
     *
     * @param request
     * @return
     */
    public boolean compareSign(HttpServletRequest request) {
        JSONObject param = JSONUtil.getJSONParam(request);
        String sign = JSONUtil.getParamRequired(param, String.class, "sign");
       // ...业务代码
        return s.equals(sign);
    }

 	/**
     * APP解密参数
     *
     * @param json
     * @return
     */
    public JSONObject getJsonParam(String json) {
        JSONObject jsonParam = JSON.parseObject(json);
        String aos = jsonParam.getString("aos");
        String params = jsonParam.getString("params");
        String param = null;
        if (jsonParam != null && !StringUtils.isEmpty(aos) && !StringUtils.isEmpty(params)) {
            String key = RSA.rsaDecrypt(aos, "自定义的私钥");
            if (StringUtils.isBlank(key)) {
                return null;
            }
            try {
                param = AES256.decrypt(params, key);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (StringUtils.isBlank(param)) {
                return null;
            }
        }
        if (StringUtils.isBlank(param)) {
            return null;
        }
        return JSONObject.parseObject(param);
    }

}

思路都在代码中备注了,就是在过滤器中,一层层解析,比如token等,然后再分别解析两种请求的参数,放入params里,其中用到的两个工具类如下

JSONUtil

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.MissingServletRequestParameterException;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class JSONUtil {

    public static JSONObject getJSONParam(HttpServletRequest request){
        Map<String, String[]> parameterMap = request.getParameterMap();
        JSONObject returnObject = new JSONObject();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String value = "";
            String[] values = entry.getValue();
            if (values != null){
                for (String s : values) {
                    value = s + ",";
                }
                value = value.substring(0, value.length() - 1);
            }
            returnObject.put(entry.getKey(), value);
        }
        return returnObject;
    }

    public static<T> T getParam(JSONObject param, Class<T> tClass, String key){
        if (param == null) {
            return null;
        }
        return param.getObject(key, tClass);
    }

    public static<T> T getParamRequired(JSONObject param, Class<T> tClass, String key){
        if (param == null) {
            throw new RuntimeException(getErrMsg(key));
        }
        T object = param.getObject(key, tClass);
        if (object == null){
            throw new RuntimeException(getErrMsg(key));
        }
        return object;
    }


    private static String getErrMsg(String key) {
        return "参数" + key + "必填";
    }
}

RequestBodyUtil

import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;

/**
 * 解析Body数据
 */
public class RequestBodyUtil {
    private static final int BUFFER_SIZE = 1024 * 8;

    private RequestBodyUtil(){}

    public static String read(Reader reader) throws IOException {
        StringWriter writer = new StringWriter();
        try {
            write(reader, writer);
            return writer.getBuffer().toString();
        } finally {
            writer.close();
        }
    }

    public static long write(Reader reader, Writer writer) throws IOException {
        return write(reader, writer, BUFFER_SIZE);
    }

    public static long write(Reader reader, Writer writer, int bufferSize) throws IOException {
        int read;
        long total = 0;
        char[] buf = new char[BUFFER_SIZE];
        while ((read = reader.read(buf)) != -1) {
            writer.write(buf, 0, read);
            total += read;
        }
        return total;
    }
}
最后

注册过滤器我就不说了,SpringBoot注册过滤器方式很多,看如何在控制器中接收参数

@PostMapping("/test")
    public Result test(@RequestParam String params){
        System.out.println("解密后的参数:" + params);
        return ResponseMsgUtil.success(params);
    }

名字只要和过滤器中自定义的Map里的Key对应,就会被拿到参数

收藏 (0)
评论列表
正在载入评论列表...
我是有底线的
为您推荐
    暂时没有数据