ToB企服应用市场:ToB评测及商务社交产业平台

标题: 接口安全防线加解密:springboot 全局-指定接口解密(同时支持参数在body和 [打印本页]

作者: 九天猎人    时间: 2024-10-26 16:39
标题: 接口安全防线加解密:springboot 全局-指定接口解密(同时支持参数在body和
接口安全防线加解密:springboot 全局/指定接口解密(同时支持参数在body和param)


上风:通过注解形式,不必要改变原接口请求参数,在拦截器里面把加密数据解密为原接口请求参数。同时支持application/x-www-form-

urlencoded和application/json 的解密
1.原理

1.1.过滤器,过滤所有请求,使用HttpServletRequestWrapper解决request中流读取一次的处理,方便后续修改请求内容

1.2.自界说注解,通过自界说注解可以标识,指定哪些接口在拦截器中处理数据

1.3.拦截器,拦截带有指定注解的请求,把数据进行加密解密后返回处理


2.支持范围

2.1.实际可以自己改造适合多种环境处理,已支持以下

1.application/json 加密参数在body
2.application/x-www-form-urlencoded 支持参数在body大概在param
2.2.为什么不用RequestBodyAdvice

1.因为RequestBodyAdvice只支持body内容的数据加解密处理,具有局限性。

3.详细实现代码

3.1. 过滤器(目的读取request,为后续自界说数据做铺垫)

  1. /**
  2. * @Author: bright chen
  3. */
  4. @WebFilter(value = "/*", filterName = "uriFormatFilter")
  5. public class UriFormatFilter extends OncePerRequestFilter {
  6.     @Override
  7.     protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
  8.         String uri = httpServletRequest.getRequestURI();
  9.         String newUri = uri.replace("//","/");
  10.         httpServletRequest = new HttpServletRequestWrapper(httpServletRequest){
  11.             @Override
  12.             public String getRequestURI() {
  13.                 return newUri;
  14.             }
  15.         };
  16.         ServletRequest requestWrapper=new RequestWrapper(httpServletRequest);
  17.         if(requestWrapper!=null){
  18.             filterChain.doFilter(requestWrapper,httpServletResponse);
  19.         }else{
  20.             filterChain.doFilter(httpServletRequest, httpServletResponse);
  21.         }
  22.     }
  23. }
复制代码
3.2.自界说HttpServletRequestWrapper(重点1,param赋值,body赋值,param pojo读取时赋值)

  1. /**
  2. * @Author: bright chen
  3. */
  4. public class RequestWrapper extends HttpServletRequestWrapper {
  5.     private String body;
  6.     private Map<String, String[]> params = new HashMap<String, String[]>();
  7.     public RequestWrapper(HttpServletRequest request) {
  8.         super(request);
  9.         StringBuilder stringBuilder = new StringBuilder();
  10.         BufferedReader bufferedReader = null;
  11.         InputStream inputStream = null;
  12.         try {
  13.             inputStream = request.getInputStream();
  14.             if (inputStream != null) {
  15.                 bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  16.                 char[] charBuffer = new char[128];
  17.                 int bytesRead = -1;
  18.                 while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
  19.                     stringBuilder.append(charBuffer, 0, bytesRead);
  20.                 }
  21.             } else {
  22.                 stringBuilder.append("");
  23.             }
  24.         } catch (IOException ex) {
  25.         } finally {
  26.             if (inputStream != null) {
  27.                 try {
  28.                     inputStream.close();
  29.                 } catch (IOException e) {
  30.                     e.printStackTrace();
  31.                 }
  32.             }
  33.             if (bufferedReader != null) {
  34.                 try {
  35.                     bufferedReader.close();
  36.                 } catch (IOException e) {
  37.                     e.printStackTrace();
  38.                 }
  39.             }
  40.         }
  41.         body = stringBuilder.toString();
  42.         this.params.putAll(request.getParameterMap());
  43.     }
  44.     @Override
  45.     public Map<String, String[]> getParameterMap() {
  46.         return params;
  47.     }
  48.     @Override
  49.     public ServletInputStream getInputStream() throws IOException {
  50.         final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
  51.         ServletInputStream servletInputStream = new ServletInputStream() {
  52.             @Override
  53.             public boolean isFinished() {
  54.                 return false;
  55.             }
  56.             @Override
  57.             public boolean isReady() {
  58.                 return false;
  59.             }
  60.             @Override
  61.             public void setReadListener(ReadListener readListener) {
  62.             }
  63.             @Override
  64.             public int read() throws IOException {
  65.                 return byteArrayInputStream.read();
  66.             }
  67.         };
  68.         return servletInputStream;
  69.     }
  70.     // 重载一个构造方法
  71.     public RequestWrapper(HttpServletRequest request, Map<String, Object> extendParams, String body) {
  72.         this(request);
  73.         if (body != null && body.length() > 0) {
  74.             setBody(body);
  75.         }
  76.         if (extendParams.size() > 0) {
  77.             addAllParameters(extendParams);// 这里将扩展参数写入参数表
  78.         }
  79.     }
  80.     @Override
  81.     public BufferedReader getReader() throws IOException {
  82.         return new BufferedReader(new InputStreamReader(this.getInputStream()));
  83.     }
  84.     public String getBody() {
  85.         return this.body;
  86.     }
  87.     // 赋值给body字段
  88.     public void setBody(String body) {
  89.         this.body = body;
  90.     }
  91.     @Override
  92.     public String getParameter(String name) {// 重写getParameter,代表参数从当前类中的map获取
  93.         String[] values = params.get(name);
  94.         if (values == null || values.length == 0) {
  95.             return null;
  96.         }
  97.         return values[0];
  98.     }
  99.     public String[] getParameterValues(String name) {// 同上
  100.         return params.get(name);
  101.     }
  102.     /**
  103.      * 参数为pojo类型时,会通过此方法获取所有的请求参数并进行遍历,对pojo属性赋值
  104.      *
  105.      * @return
  106.      */
  107.     @Override
  108.     public Enumeration<String> getParameterNames() {// 同上
  109.         ArrayList<String> list = list = new ArrayList<>();
  110.         for (Map.Entry<String, String[]> entry : params.entrySet()) {
  111.             list.add(entry.getKey());
  112.         }
  113.         return Collections.enumeration(list);
  114.     }
  115.     public void addAllParameters(Map<String, Object> otherParams) {// 增加多个参数
  116.         for (Map.Entry<String, Object> entry : otherParams.entrySet()) {
  117.             addParameter(entry.getKey(), entry.getValue());
  118.         }
  119.     }
  120.     public void addParameter(String name, Object value) {// 增加参数
  121.         if (value != null) {
  122.             if (value instanceof String[]) {
  123.                 params.put(name, (String[]) value);
  124.             } else if (value instanceof String) {
  125.                 params.put(name, new String[]{(String) value});
  126.             } else {
  127.                 params.put(name, new String[]{String.valueOf(value)});
  128.             }
  129.         }
  130.     }
  131. }
复制代码
3.3.自界说注解 @DecryptApi,后期在controller层带上这个注解则该controller层接口会被过滤器及拦截器进行解密处理。

  1. /**
  2. * 支持params和body的解密
  3. * 支持
  4. * @Author: bright chen
  5. */
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target({ElementType.METHOD,ElementType.PARAMETER})
  8. public @interface DecryptApi {
  9. }
复制代码
3.4. (核心代码) 拦截器 DecryptInterceptor,对带有@DecryptApi注解的接口数据进行解密处理后重新赋值,注意配置

拦截所有请求
  1. /**
  2. * @Author: bright chen
  3. * api 解密拦截器
  4. */
  5. public class DecryptInterceptor extends HandlerInterceptorAdapter {
  6.     private static Logger logger = LogManager.getLogger(DecryptInterceptor.class);
  7.     @Autowired
  8.     private EncryptProperties encryptProperties;
  9.     /**
  10.      * Controller之前执行
  11.      * preHandle:拦截于请求刚进入时,进行判断,需要boolean返回值,如果返回true将继续执行,如果返回false,将不进行执行。一般用于登录校验
  12.      * 1.当preHandle方法返回false时,从当前拦截器往回执行所有拦截器的afterCompletion方法,再退出拦截器链。也就是说,请求不继续往下传了,直接沿着来的链往回跑。
  13.      * 2.当preHandle方法全为true时,执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链,运行所有拦截器的postHandle方法,
  14.      * 完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.
  15.      */
  16.     @Override
  17.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  18.         try {
  19.             if (handler instanceof HandlerMethod) {
  20.                 HandlerMethod method = (HandlerMethod) handler;
  21.                 // RequireLogin annotation = method.getMethodAnnotation(RequireLogin.class);
  22.                 if (method.hasMethodAnnotation(DecryptApi.class)) {
  23.                     // 需要对数据进行加密解密
  24.                     // 1.对application/json类型
  25.                     String contentType = request.getContentType();
  26.                     if (contentType == null && !"GET".equals(request.getMethod())) {
  27.                         // 请求不通过,返回错误信息给客户端
  28.                         responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
  29.                         return false;
  30.                     }
  31.                     String requestBody = null;
  32.                     boolean shouldEncrypt = false;
  33.                     if ((contentType != null && StringUtils.substringMatch(contentType, 0,
  34.                             MediaType.APPLICATION_FORM_URLENCODED_VALUE)) || "GET".equals(request.getMethod())) {
  35.                         // 1.application/x-www-form-urlencoded 支持参数在body或者在param
  36.                         shouldEncrypt = true;
  37.                         requestBody = convertFormToString(request);
  38.                         if (requestBody == null || "{}".equals(requestBody)) {
  39.                             requestBody = URLDecoder.decode(convertInputStreamToString(request.getInputStream()),
  40.                                     "UTF-8");
  41.                             List<String> uriToList =
  42.                                     Stream.of(requestBody.split("&")).map(elem -> new String(elem)).collect(Collectors.toList());
  43.                             Map<String, String> uriToListToMap = new HashMap<>();
  44.                             for (String individualElement : uriToList) {
  45.                                 if (individualElement.split("=")[0] != null && !"".equals(individualElement.split("=")[0])) {
  46.                                     uriToListToMap.put(individualElement.split("=")[0],
  47.                                             individualElement.substring(individualElement.split("=")[0].length() + 1));
  48.                                 }
  49.                             }
  50.                             requestBody = JSONObject.toJSONString(uriToListToMap);
  51.                         }
  52.                     } else if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
  53.                         // application/json 支持加密参数在body
  54.                         shouldEncrypt = true;
  55.                         requestBody = convertInputStreamToString(request.getInputStream());
  56.                     }
  57.                     if (requestBody == null || "{}".equals(requestBody)||!shouldEncrypt) {
  58.                         return true;
  59.                     } else {
  60.                         String result = decodeApi(JSON.parseObject(requestBody, StdRequestApi.class),
  61.                                 encryptProperties.getPrivateKey());
  62.                         if (result == null) {
  63.                             // 请求不通过,返回错误信息给客户端
  64.                             responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
  65.                             return false;
  66.                         }
  67.                         JSONObject jasonObject = JSONObject.parseObject(result);
  68.                         Map map = (Map) jasonObject;
  69.                         if (request instanceof RequestWrapper) {
  70.                             RequestWrapper requestWrapper = (RequestWrapper) request;
  71.                             requestWrapper.setBody(result);
  72.                             requestWrapper.addAllParameters(map);
  73.                             // requestWrapper = new RequestWrapper(request, map, result);
  74.                             return true;
  75.                         }
  76.                     }
  77.                 } else {
  78.                     String contentType = request.getContentType();
  79.                     if (contentType != null && contentType.length() > 0 && StringUtils.substringMatch(contentType, 0,
  80.                             MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
  81.                         // 1.application/x-www-form-urlencoded 支持参数在body或者在param
  82.                         String requestBody = convertFormToString(request);
  83.                         if (requestBody == null || "{}".equals(requestBody)) {
  84.                             // 把流数据放进param中,不解密
  85.                             requestBody = URLDecoder.decode(convertInputStreamToString(request.getInputStream()),
  86.                                     "UTF-8");
  87.                             List<String> uriToList =
  88.                                     Stream.of(requestBody.split("&")).map(elem -> new String(elem)).collect(Collectors.toList());
  89.                             Map<String, Object> uriToListToMap = new HashMap<>();
  90.                             for (String individualElement : uriToList) {
  91.                                 if (individualElement.split("=")[0] != null && !"".equals(individualElement.split("=")[0])) {
  92.                                     uriToListToMap.put(individualElement.split("=")[0],
  93.                                             individualElement.substring(individualElement.split("=")[0].length() + 1));
  94.                                 }
  95.                             }
  96.                             if (request instanceof RequestWrapper) {
  97.                                 RequestWrapper requestWrapper = (RequestWrapper) request;
  98.                                 requestWrapper.setBody(requestBody);
  99.                                 requestWrapper.addAllParameters(uriToListToMap);
  100.                                 return true;
  101.                             }
  102.                         }
  103.                     }
  104.                 }
  105.                 return true;
  106.             }
  107.             return true;
  108.         } catch (Exception e) {
  109.             e.printStackTrace();
  110.             logger.error(e.getMessage() + "异常地址:" + request.getServletPath());
  111.             responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
  112.             return false;
  113.         }
  114.     }
  115.     /**
  116.      * 返回信息给客户端
  117.      *
  118.      * @param response
  119.      * @param tResponse
  120.      */
  121.     private void responseResult(HttpServletResponse response, TResponse tResponse) throws IOException {
  122.         response.setContentType(HttpConstant.CONTENT_TYPE_JSON);
  123.         String json = JSONObject.toJSONString(tResponse);
  124.         PrintWriter out = response.getWriter();
  125.         out.print(json);
  126.         out.flush();
  127.         out.close();
  128.     }
  129.     private String convertFormToString(HttpServletRequest request) {
  130.         Map<String, String> result = new HashMap<>(8);
  131.         Enumeration<String> parameterNames = request.getParameterNames();
  132.         while (parameterNames.hasMoreElements()) {
  133.             String name = parameterNames.nextElement();
  134.             result.put(name, request.getParameter(name));
  135.         }
  136.         try {
  137.             return JSON.toJSONString(result);
  138.         } catch (Exception e) {
  139.             throw new IllegalArgumentException(e);
  140.         }
  141.     }
  142.     private String convertInputStreamToString(InputStream inputStream) throws IOException {
  143.         return StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
  144.     }
  145.     public String decodeApi(StdRequestApi stdRequestApi, String apiPrivateKey) {
  146.         try {
  147.             // 1.rsa解密
  148.             // 2.AES验签
  149.             // 3.AES解密
  150.             return deData;
  151.         } catch (Exception e) {
  152.             e.printStackTrace();
  153.             return null;
  154.         }
  155.     }
  156.     /**
  157.      * 返回信息给客户端
  158.      *
  159.      * @param response
  160.      * @param out
  161.      * @param tResponse
  162.      */
  163.     private void responseResult(HttpServletResponse response, PrintWriter out, TResponse tResponse) {
  164.         response.setContentType(HttpConstant.CONTENT_TYPE_JSON);
  165.         String json = JSONObject.toJSONString(tResponse);
  166.         out.print(json);
  167.         out.flush();
  168.         out.close();
  169.     }
  170. }
复制代码
3.5.公钥私钥配置读取

  1. /**
  2. * @Author: bright chen
  3. */
  4. @Component
  5. @ConfigurationProperties(prefix = "api")
  6. public class EncryptProperties {
  7.     private String privateKey;
  8.     private String publicKey;
  9.     public String getPrivateKey() {
  10.         return privateKey;
  11.     }
  12.     public void setPrivateKey(String privateKey) {
  13.         this.privateKey = privateKey;
  14.     }
  15.     public String getPublicKey() {
  16.         return publicKey;
  17.     }
  18.     public void setPublicKey(String publicKey) {
  19.         this.publicKey = publicKey;
  20.     }
  21. }
复制代码

PS:本文编写于公司产物被黑客入侵后的处理方案之一,详细加密解密逻辑不在本文中报告
下一篇:接口安全防线注解加解密(二):加密请求异常后请求/error处理
末了

从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同砚们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门水平而已,能力越强时机才越多。
因为入门学习阶段知识点比较杂,所以我讲得比较笼统,各人如果有不懂的地方可以找我咨询,我保证知无不言言无不尽,必要相干资料也可以找我要,我的网盘里一大堆资料都在吃灰呢。
干货主要有:
①1000+CTF历届题库(主流和经典的应该都有了)
②CTF技术文档(最全中文版)
③项目源码(四五十个有趣且经典的练手项目及源码)
④ CTF大赛、web安全、渗出测试方面的视频(适合小白学习)
⑤ 网络安全学习路线图(告别不入流的学习)
⑥ CTF/渗出测试工具镜像文件大全
⑦ 2023密码学/隐身术/PWN技术手册大全
如果你对网络安全入门感爱好,那么你必要的话可以点击这里




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4