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

网友投稿 337 2022-12-08

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 params = new HashMap<>(16);

public ParameterRequest(HttpServletRequest request) throws IOException {

super(request);

this.params.putAll(request.getParameterMap());

}

/**

* 重载一个构造方法

*

* @param request

* @param extendParams

*/

public PuptSTIkarameterRequest(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;

@Slf4j

public 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; chahttp://rset=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.HttpServletRequeuptSTIkst;

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.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的依赖

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 //定义一个切面

@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在启动时会会自动去加载这些配置,实现相应的配置功能。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot入门原理及优势分析
下一篇:Java 模拟真正的并发请求详情
相关文章

 发表评论

暂时没有评论,来抢沙发吧~