SpringBoot笔记:统一请求参数修改(HttpServletRequest流复制),加解密参数也可参考处理

需求

    需要进行统一的解密请求 header 头里面的关键字 encryKey ,将解密出来的值赋给 provinceId 并传递给后端的每一个请求接口,并通过 provinceId 字段进行数据分权

实现思路

1、首先设置过滤器将所有请求过滤
2、获取请求头 header 头里面的关键字 encryKey ,将值解密出来
3、解析请求报文,将 encryKey 解密出来的值赋给 provinceId
4、重新封装请求报文,进行后续的请求操作

实战演练

实现过滤器Filter

package com.demo.filter;

import cn.hutool.core.codec.Base64;
import com.demo.wrapper.MyRequestWrapper;
import com.fasterxml.jackson.core.io.JsonEOFException;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.RequestFacade;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 过滤器,处理GET相关请求
 *
 * @author leo825
 * @date 2022/11/23 17:15
 */
@Slf4j
@Component
@WebFilter(filterName = "decryptGetRequestFilter", urlPatterns = {
    
    "/*"})
public class DecryptGetRequestFilter implements Filter {
    
    

    @Override
    public void init(FilterConfig filterConfig) {
    
    
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        long start = System.currentTimeMillis();
        //获取HttpServletRequest对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if ("GET".equalsIgnoreCase(httpServletRequest.getMethod())) {
    
    
            String encryKey = httpServletRequest.getHeader("encryKey");
            Map<String, String[]> newParameterMap = new HashMap<>();
            if (StringUtils.isNotBlank(encryKey) && !"MTAw".equalsIgnoreCase(encryKey)) {
    
    
                String provinceId = Base64.decodeStr(encryKey);
                log.info("encryKey:{},  provinceId:{}", encryKey, provinceId);
                newParameterMap.put("provinceId", new String[]{
    
    provinceId});
            }
            MyRequestWrapper myRequestWrapper = new MyRequestWrapper(httpServletRequest, newParameterMap);
            chain.doFilter(myRequestWrapper, response);
        } else {
    
    
            try {
    
    
                chain.doFilter(request, response);
            } catch (HttpMessageNotReadableException httpMessageNotReadableException) {
    
    
                log.error(((RequestFacade) request).getRequestURI() + ", " + httpMessageNotReadableException.getMessage());
            } catch (JsonEOFException jsonEOFException) {
    
    
                log.error(((RequestFacade) request).getRequestURI() + ", " + jsonEOFException.getMessage());
            }
        }
        long end = System.currentTimeMillis();
        log.info("{} 接口耗时:{} ms", httpServletRequest.getRequestURI(), (end-start));
    }

    @Override
    public void destroy() {
    
    
    }

}

继承 HttpServletRequestWrapper

package com.demo.wrapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * http请求增强器,用来修改参数
 *
 * @author leo825
 * @date 2022/11/23 17:13
 */
public class MyRequestWrapper extends HttpServletRequestWrapper {
    
    

    private Map params = new HashMap<>();
    public MyRequestWrapper(HttpServletRequest request, Map newParams) {
    
    
        super(request);
        if(request.getParameterMap() != null){
    
    
            this.params.putAll(request.getParameterMap());
        }
        if(newParams != null){
    
    
            this.params.putAll(newParams);
        }
    }

    /**
     * 获取参数
     * @return
     */
    @Override
    public Map getParameterMap() {
    
    
        return params;
    }

    @Override
    public Enumeration getParameterNames() {
    
    
        Vector l = new Vector(params.keySet());
        return l.elements();
    }


    @Override
    public String[] getParameterValues(String name) {
    
    
        Object v = params.get(name);
        if (v == null) {
    
    
            return null;
        } else if (v instanceof String[]) {
    
    
            return (String[]) v;
        } else if (v instanceof String) {
    
    
            return new String[]{
    
    (String) v};
        } else {
    
    
            return new String[]{
    
    v.toString()};
        }
    }

    /**
     * 根据参数的key获取参数
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
    
    
        Object v = params.get(name);
        if (v == null) {
    
    
            return null;
        } else if (v instanceof String[]) {
    
    
            String[] strArr = (String[]) v;
            if (strArr.length > 0) {
    
    
                return strArr[0];
            } else {
    
    
                return null;
            }
        } else if (v instanceof String) {
    
    
            return (String) v;
        } else {
    
    
            return v.toString();
        }
    }
}

实现 RequestBodyAdvice 统一处理请求参数

package com.demo.handler;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.Arrays;

/**
 * 统一处理请求参数
 *
 * @author leo825
 * @date 2022/11/23 11:17
 */
@RestControllerAdvice
@Slf4j
public class DecryptRequestBodyHandler implements RequestBodyAdvice {
    
    

    /**
     *  省份编码的别称,注意:provinceOrCityId 级别是1的时候代表省份
     */
   public static String[] provinceIdNickNames = {
    
    "provinceId", "provinceOrCityId"};

    /**
     * 该方法用于判断当前请求,是否要执行beforeBodyRead方法
     * methodParameter方法的参数对象
     * type方法的参数类型
     * aClass 将会使用到的Http消息转换器类类型
     * 注意:此判断方法,会在beforeBodyRead 和 afterBodyRead方法前都触发一次。
     *
     * @return 返回true则会执行beforeBodyRead
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    
    
        return true;
    }

    /**
     * 在Http消息转换器执转换,之前执行
     * inputMessage 客户端的请求数据
     * parameter方法的参数对象
     * targetType方法的参数类型
     * converterType 将会使用到的Http消息转换器类类型
     * @return 返回 一个自定义的HttpInputMessage
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    
    
        // 如果body是空内容直接返回原来的请求
        if (inputMessage.getBody().available() <= 0) {
    
    
            return inputMessage;
        }
        // 判断文件头中不包含 encryKey 则跳过
        HttpHeaders headers = inputMessage.getHeaders();
        if (headers.get("encryKey") == null) {
    
    
            return inputMessage;
        }
        // 判断关键字 encryKey 的值是否合法或者是全国权限, MTAw 代表的是全国权限 100
        String encryKey = headers.get("encryKey").get(0);
        if (StringUtils.isBlank(encryKey) || StringUtils.isNumeric(encryKey)) {
    
    
            return inputMessage;
        }
        // 将输入流读出来,注意 body 里面的流只能读一次
        ByteArrayOutputStream requestBodyDataByte = new ByteArrayOutputStream();
        try {
    
    
            IoUtil.copy(inputMessage.getBody(), requestBodyDataByte);
        } catch (Exception e) {
    
    
            log.error("参数流拷贝失败: ", e);
            return inputMessage;
        }
        // 对包含 encryKey 关键字的接口进行处理
        ByteArrayOutputStream requestBodyDataByteNew = null;
        try {
    
    
            String provinceId = Base64.decodeStr(encryKey);
            log.info("encryKey:{},  provinceId:{}", encryKey, provinceId);
            JSONObject body = JSON.parseObject(requestBodyDataByte.toByteArray(), JSONObject.class);
            Arrays.asList(provinceIdNickNames).stream().forEach(e -> {
    
    
                if (body.containsKey(e)) {
    
    
                    body.put(e, provinceId);
                }
            });
            log.info("request: " + body.toJSONString());
            requestBodyDataByteNew = new ByteArrayOutputStream();
            IoUtil.copy(new ByteArrayInputStream(body.toJSONString().getBytes()), requestBodyDataByteNew);
        } catch (Throwable e) {
    
    
            log.error("encryKey 转换异常 ", e);
        }
        // 如果上述发生异常,仍然使用原来的请求内容
        requestBodyDataByte = requestBodyDataByteNew != null ? requestBodyDataByteNew : requestBodyDataByte;
        InputStream rawInputStream = new ByteArrayInputStream(requestBodyDataByte.toByteArray());
        inputMessage.getHeaders().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(rawInputStream.available()));
        return new HttpInputMessage() {
    
    
            @Override
            public HttpHeaders getHeaders() {
    
    
                return inputMessage.getHeaders();
            }

            @Override
            public InputStream getBody() throws IOException {
    
    
                return rawInputStream;
            }
        };
    }

    /**
     * 在Http消息转换器执转换,之后执行
     * body 转换后的对象
     * inputMessage 客户端的请求数据
     * parameter handler方法的参数类型
     * targetType handler方法的参数类型
     * converterType 使用的Http消息转换器类类型
     * @return 返回一个新的对象
     */
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    
    
        return body;
    }

    /**
     * 参数与afterBodyRead相同,不过这个方法处理的是,body为空的情况
     */
    @Override
    public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    
    
        return body;
    }
}

测试代码

package com.demo.controller;

import com.demo.base.Result;
import com.demo.vo.ProvinceVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * controller 测试
 *
 */
@RestController
@RequestMapping("/province")
public class ProvinceController {
    
    

    /**
     *
     * @param provinceVo
     * @return
     */
    @PostMapping("/getProvinceId")
    public Result getProvinceId(@RequestBody ProvinceVo provinceVo) {
    
    
        return new Result().ok(provinceVo);
    }
}

测试效果

测试参数
header:
encryKey: MTAw(解密后是100)

body:

{
    
    
	"cityId": "12411",
	"cityName": "",
	"provinceId": "124",
	"provinceName": ""
}

1
2

测试结果:
3
附源码地址:https://gitee.com/leo825/springboot-learning-parents.git

猜你喜欢

转载自blog.csdn.net/u011047968/article/details/128814481
今日推荐