千家信息网

spring boot如何实现在request里解密参数返回

发表于:2025-01-16 作者:千家信息网编辑
千家信息网最后更新 2025年01月16日,这篇文章将为大家详细讲解有关spring boot如何实现在request里解密参数返回,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言有个业务需求,一个请求来源
千家信息网最后更新 2025年01月16日spring boot如何实现在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 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 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 otherParams) {        for (Map.Entry 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;@Slf4jpublic class WebParamFilter implements Filter {    private static final String OPTIONS = "OPTIONS";    @Value("${jwt.info.urlPatterns}")    private List 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 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 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 parameterMap = request.getParameterMap();        JSONObject returnObject = new JSONObject();        for (Map.Entry 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 getParam(JSONObject param, Class tClass, String key){        if (param == null) {            return null;        }        return param.getObject(key, tClass);    }    public static T getParamRequired(JSONObject param, Class 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对应,就会被拿到参数

Spring boot配置Aop获取controller里的request中的参数及其返回值

示例:

当前url:http://localhost:8080/CarsiLogCenter_new/idpstat.jsp?action=idp.sptopn

request.getRequestURL() http://localhost:8080/CarsiLogCenter_new/idpstat.jsprequest.getRequestURI() /CarsiLogCenter_new/idpstat.jsprequest.getContextPath()/CarsiLogCenter_newrequest.getServletPath() /idpstat.jsprequest.getQueryString() action=idp.sptopn
public static String getLastAccessUrl(HttpServletRequest request) {        StringBuffer requestURL = request.getRequestURI();        String queryString = request.getQueryString();        if (queryString == null) {            return requestURL.toString();        }        return requestURL + "?" + queryString;    }

1、request.getRequestURL()

  • 返回的是完整的url,包括Http协议,端口号,servlet名字和映射路径,但它不包含请求参数。

2、request.getRequestURI()

  • 得到的是request URL的部分值,并且web容器没有decode过的

3、request.getContextPath()

  • 返回 the context of the request.

4、request.getServletPath()

  • 返回调用servlet的部分url.

5、request.getQueryString()

  • 返回url路径后面的查询字符串

首先在你的Maven的pom文件里加入aop的依赖

        org.springframework.boot      spring-boot-starter-aop  

在spring boot里面一切配置都是很简单的,

下面为我所有被请求到的controller加上Aop的功能

看码吧:

import javax.servlet.http.HttpServletRequest;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.context.annotation.Configuration;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.google.gson.Gson;import org.slf4j.Logger;import org.slf4j.LoggerFactory;;@Aspect   //定义一个切面@Configurationpublic class LogRecordAspect {private static final Logger logger = LoggerFactory.getLogger(UserInterceptor.class);    // 定义切点Pointcut    @Pointcut("execution(* com.jiaobuchong.web.*Controller.*(..))")    public void excudeService() {    }    @Around("excudeService()")    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {        RequestAttributes ra = RequestContextHolder.getRequestAttributes();        ServletRequestAttributes sra = (ServletRequestAttributes) ra;        HttpServletRequest request = sra.getRequest();        String url = request.getRequestURL().toString();        String method = request.getMethod();        String uri = request.getRequestURI();        String queryString = request.getQueryString();        logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);        // result的值就是被拦截方法的返回值        Object result = pjp.proceed();        Gson gson = new Gson();        logger.info("请求结束,controller的返回值是 " + gson.toJson(result));        return result;    }}

只要加上上面这个类,Aop就算配置好了,不信,去访问以下你的Controller试试。对比以前配置aop的方式(xml文件),现在的配置都到Java代码里来了,@Configuration这个Annotation就是JavaConfig的典型代表,Spring boot在启动时会会自动去加载这些配置,实现相应的配置功能。

关于"spring boot如何实现在request里解密参数返回"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

0