spring|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类,继承这个类重写两个方法就可以了。
【spring|spring boot实现在request里解密参数返回】
代码块
重写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); }@Overridepublic String getParameter(String name) {String[] values = params.get(name); if (values == null || values.length == 0) {return null; }return values[0]; }@Overridepublic 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; @Overridepublic void init(FilterConfig filterConfig) throws ServletException {SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext()); }@Overridepublic 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是APPString 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); }}@Overridepublic 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 valuehttps://www.it610.com/article/= ""; String[] values = entry.getValue(); if (values != null){for (String s : values) {value = https://www.it610.com/article/s +","; }value = https://www.it610.com/article/value.substring(0, value.length() - 1); }returnObject.put(entry.getKey(), value); }return returnObject; }public staticT 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名字和映射路径,但它不包含请求参数。
request.getRequestURI()
- 得到的是request URL的部分值,并且web容器没有decode过的
request.getContextPath()
- 返回 the context of the request.
request.getServletPath()
- 返回调用servlet的部分url.
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在启动时会会自动去加载这些配置,实现相应的配置功能。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 科学养胃,别被忽悠,其实真的很简单
- 其实你就是个普通人
- 海院(实干是海院风景(上))
- 你眼里的不公平,其实很公平
- 孩子不是实现父母欲望的工具——林哈夫
- SpringBoot调用公共模块的自定义注解失效的解决