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

目录
  • spring boot在request里解密参数返回
    • 前言
    • 代码块
    • 最后
  • Spring boot配置Aop获取controller里的request中的参数及其返回值
    • 首先在你的Maven的pom文件里加入aop的依赖
    • 下面为我所有被请求到的controller加上Aop的功能

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对应,就会被拿到参数

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

示例:

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

request.getRequestURL() http://localhost:8080/CarsiLogCenter_new/idpstat.jsp
request.getRequestURI() /CarsiLogCenter_new/idpstat.jsp
request.getContextPath()/CarsiLogCenter_new
request.getServletPath() /idpstat.jsp
request.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的依赖

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>

在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   //定义一个切面
@Configuration
public 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在启动时会会自动去加载这些配置,实现相应的配置功能。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2021-09-15

Spring Boot 接口参数加密解密的实现方法

因为有小伙伴刚好问到这个问题,松哥就抽空撸一篇文章和大家聊聊这个话题. 加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据.不过 SpringMVC 中给我们提供了 ResponseBodyAdvice 和 RequestBodyAdvice,利用这两个工具可以对请求和响应进行预处理,非常方便. 所以今天这篇文章有两个目的: 分享参数/响应加解密的思路. 分享 Response

Spring boot @RequestBody数据传递过程详解

这篇文章主要介绍了Spring boot @RequestBody数据传递过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 @RequestBody需要接的参数是一个string化的json @RequestBody,要读取的数据在请求体里,所以要发post请求,还要将Content-Type设置为application/json java的api 参数为JSONObject,获取到的参数处理 @PostMapping("/combine

SpringBoot实现接口数据的加解密功能

一.加密方案介绍 对接口的加密解密操作主要有下面两种方式: 自定义消息转换器 优势:仅需实现接口,配置简单. 劣势:仅能对同一类型的MediaType进行加解密操作,不灵活. 使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice 优势:可以按照请求的Referrer.Header或url进行判断,按照特定需要进行加密解密. 比如在一个项目升级的时候,新开发功能的接口需要加解密,老功能模块走之前的逻辑不加密,这时候就只能选择上面的第二种方式了,下面主要

SpringBoot接口加密解密统一处理

我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密).返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求. 将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用Controller的ControllerAdvice来拦截所有的请求,在其中判断是否需要加密解密,即可达到要求. 使用方法:使用 DecryptRequest 和 EncryptResponse 注解即可,可以放在

PHP的RSA加密解密方法以及开发接口使用

网络安全问题很重要,尤其是保证数据安全,遇到很多在写接口的程序员直接都是明文数据传输,在我看来这是很不专业的.本人提倡经过接口的数据都要进行加密解密之后进行使用. 这篇文章主要介绍使用PHP开发接口,数据实现RSA加密解密后使用,实例分析了PHP自定义RSA类实现加密与解密的技巧,非常具有实用价值,需要的朋友可以参考下. 简单介绍RSA RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.但是有不少新手对它不太了解.下面仅作简要介绍.RSA是第一个比较完善的公开密钥算法,它既能

Springboot实现密码的加密解密

现今对于大多数公司来说,信息安全工作尤为重要,就像京东,阿里巴巴这样的大公司来说,信息安全是最为重要的一个话题,举个简单的例子: 就像这样的密码公开化,很容易造成一定的信息的泄露.所以今天我们要讲的就是如何来实现密码的加密和解密来提高数据的安全性. 在这首先要引入springboot融合mybatis的知识,如果有这方面不懂得同学,就要首先看一看这方面的知识: 推荐大家一个比较好的博客: 程序猿DD-翟永超 http://blog.didispace.com/springbootmybatis/

在SpringBoot中通过jasypt进行加密解密的方法

1.用途 在SpringBoot中,通过jasypt可以进行加密解密. 这个是双向的, 且可以配置密钥. 2.使用: 2.1通过UT创建工具类,并认识jasypt import org.jasypt.util.text.BasicTextEncryptor; import org.junit.Test; public class UtilTests { @Test public void jasyptTest() { BasicTextEncryptor encryptor = new Basi

java实现Base64加密解密算法

Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045-RFC2049,上面有MIME的详细规范.Base64编码可用于在HTTP环境下传递较长的标识信息.例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数.在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式.

java中使用DES加密解密实例

在前面介绍了一些加密解密类的使用,这里综合起来做一个简单的测试,代码如下: MainActivity: 复制代码 代码如下: package com.home.testdes; import android.os.Bundle;import android.util.Log;import android.app.Activity; public class MainActivity extends Activity { @Override protected void onCreate(Bund

python中base64加密解密方法实例分析

本文实例讲述了python中base64加密解密方法.分享给大家供大家参考.具体分析如下: 一.base64 Base64是一种基于64个可打印字符来表示二进制数据的表示方法.由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符.三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示.它可用来作为电子邮件的传输编码.在Base64中的可打印字符包括字母A-Z.a-z.数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中而不同.编码后的

PHP对称加密函数实现数据的加密解密

项目中有一个地方用到了将用户ID加密.传至下个接点进行反解的需求.(原谅我不能透漏太多-_-!),第一个想到的就是康盛Ucenter中的一个函数,后来搜了下,在简明魔法中也找到了个简单的方法,遂整合了下,形成了自己使用的函数. 一.对称加密 发送方将明文使用密钥和算法处理成密文发送出去,接收方使用密钥和算法将密文处理成明文,发收信双方使用同一个密钥对数据进行加密和解密. 因为使用同一个密钥加密.解密,所以安全性上不仅与算法有关,密钥的安全也很重要. 当然并不是密钥越复杂越好,相反密钥通常比较小的

python实现的AES双向对称加密解密与用法分析

本文实例讲述了python实现的AES双向对称加密解密与用法.分享给大家供大家参考,具体如下: 高级加密标准(Advanced Encryption Standard,AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用.经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准.2006年,高级

android中对文件加密解密的实现

现在项目里面有一个需求,本项目里面下载的视频和文档都不允许通过其他的播放器播放,在培训机构里面这样的需求很多.防止有人交一份钱,把所有的课件就拷给了别人.这样的事情培训机构肯定是不愿意的.现在我项目里面也出了这么个需求.下面介绍一下我的实现. 文件加解密的流程及原理 1.加密方法:存储文件时,从输入流中截取文件的字节数组,对字节数组进行加密,至于加密的方式和算法就可以视需求而定了,然后把加密后的字节数组写入到文件中,最后生成加密后的文件: 2.解密方法:同加密方法一样,只不过是对字节数据进行解密