spring boot在request里解密参数返回
前言
有个业务需求,一个请求来源web,一个请求来源APP,web需求验证签名,APP的参数是经过加密,所以出现了两个Controller,除了解密获取参数方式不一样,其他内容一模一样,这样不太合理,所以我决定重构。
思路:既然只是解密不一样,获取到的参数是一样的,那可以写一个过滤器,在里面就把参数解密好,然后返回
Spring Boot在请求的时候是不允许直接修改HttpServletRequest里的paramsMap参数的,但是提供了一个HttpServletRequestWrapper类,继承这个类重写两个方法就可以了。
代码块
重写HttpServletRequestWrapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
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
过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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注册过滤器方式很多,看如何在控制器中接收参数
1
2
3
4
5
|
@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
1
2
3
4
5
|
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 |
1
2
3
4
5
6
7
8
|
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的依赖
1
2
3
4
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-aop</ artifactId > </ dependency > |
在spring boot里面一切配置都是很简单的,
下面为我所有被请求到的controller加上Aop的功能
看码吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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在启动时会会自动去加载这些配置,实现相应的配置功能。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/m0_37701381/article/details/103303154