好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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测试数据mons.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测试数据pile(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测试数据mons.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

查看更多关于spring boot实现在request里解密参数返回的详细内容...

  阅读:19次