java通过拦截器校验进行参数校验,在接口调用前进行校验,解决jar包中class文件无法修改,但不想修改源码的场景

1.定义参数拦截接口ParamsCheckInter.java

package cn.togeek.config;

public interface ParamsCheckInter<T> {

   T doCheck(Object... args);

}

2.定义参数校验实现逻辑 ScContractDecomposeWayInterHandler.java

package cn.togeek.config;

import cn.togeek.entity.ScContractDecomposeWay;
import cn.togeek.exception.ClientError;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public class ScContractDecomposeWayInterHandler implements ParamsCheckInter<Boolean> {

   @Override
   public Boolean doCheck(Object... args) {
      ScContractDecomposeWay way = (ScContractDecomposeWay) args[0];
      String info = way.getInfo();
      Assert.isTrue(StringUtils.hasLength(info),()->{throw new ClientError("分解方式不能为空");});
      return way.isInfoOk();
   }

}

3.重写过滤器,避免request对象读取流后,controller层参数失效,原因是流只能读取一次

3.1 request包装类RequestWrapper.java

package cn.togeek.config;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
 
/**
 * @author wangyan
 * @description  获取请求参数并处理
 * @date 2019/7/18 18:38
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }
    public String  getBodyString(){
        return new String(body,Charset.forName("UTF-8"));
    }
    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
    /**
     * Description: 复制输入流</br>
     *
     * @param inputStream
     * @return</br>
     */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
 
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
 
            @Override
            public int read() throws IOException {
                return bais.read();
            }
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener readListener) {
 
            }
        };
    }
}

3.2.重新对request对象进行封装,放入到过滤器链中 RequestParamsFilter.java

package cn.togeek.config;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

/**
 * 
 * @Author TCL
 * @Date 2021/4/26 15:30
 * @Version 1.0
 */
@WebFilter(filterName="bodyReaderFilter",urlPatterns="/*")
public class RequestParamsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // do nothing
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        ServletRequest requestWrapper=null;
        if(request instanceof HttpServletRequest) {
            requestWrapper=new RequestWrapper((HttpServletRequest)request);
        }
        if(requestWrapper==null) {
            chain.doFilter(request, response);
        }else {
            chain.doFilter(requestWrapper, response);
        }

    }

}

3.3.拦截器 ParamsCheckIntercepter.java

package cn.togeek.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import cn.togeek.util.EmpSpringContextUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.Environment;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 参数过滤拦截器
 */
@Slf4j
public class ParamsCheckIntercepter implements HandlerInterceptor {


   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      if(handler instanceof HandlerMethod){

         HandlerMethod handlerMethod = (HandlerMethod) handler;
         String method = request.getMethod();
         Environment environment = EmpSpringContextUtil.getBean(Environment.class);
         String requestUrl = request.getRequestURI();
         List<RequestInfos> list = IntercepterList.getList();
         Map<String, String[]> originRequestMap = request.getParameterMap();
         Map<String,Object> requestMap = new HashMap<>();
         for (String key : originRequestMap.keySet()) {
            String[] values = originRequestMap.get(key);
            requestMap.put(key,values[0]);
         }
         String param;
         String contextPath = environment.getProperty("server.servlet.context-path");
         if(StringUtils.isNotEmpty(contextPath)) {
            requestUrl = requestUrl.replaceAll(contextPath,"");
         }
         for(RequestInfos requestInfo : list) {
            String url = requestInfo.getUrl();
            String infoMethod = requestInfo.getMethod();
            String fullClassName = requestInfo.getFullClassName();
            if(infoMethod.equalsIgnoreCase(method) && url.equals(requestUrl)) {
               log.info("ParamsCheckIntercepter#preHandle contextPath:{},requestInfo:{}",contextPath,JSON.toJSONString(requestInfo));
               try {
                  String body =  new RequestWrapper(request).getBodyString();
                  MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
                  int length = methodParameters.length;
                  Object args[] = new Object[length];
                  if(StringUtils.isNotBlank(body)){
                     LinkedHashMap<String,String> map = JSONObject.parseObject(body, LinkedHashMap.class);
                     requestMap.putAll(map);
                  }
                  JSONObject info = new JSONObject(requestMap);
                  param = JSON.toJSONString(requestMap);
                  System.out.println("拦截器参数:"+param);
                  for(int i = 0; i < methodParameters.length; i++) {
                     MethodParameter methodParameter = methodParameters[i];
                     try {
                        args[i] = info.toJavaObject(methodParameter.getParameterType());
                     }
                     catch(Exception e) {
                        e.printStackTrace();
                     }
                  }
                  if(StringUtils.isNotEmpty(fullClassName)) {
                     Class<?> aClass = Class.forName(fullClassName);
                     Object bean = aClass.newInstance();
                     if(bean instanceof ParamsCheckInter) {
                        ParamsCheckInter<Boolean> interBean = (ParamsCheckInter<Boolean>) bean;
                        return interBean.doCheck(args);
                     }
                  }
                  return true;
               } catch (Exception e) {
                  response.setContentType("text/html;charset=UTF-8");
                  PrintWriter writer = response.getWriter();
                  writer.write(e.getMessage());
                  writer.flush();
                  writer.close();
                  return false;
               }
            }
         }
      }
      return true;
   }

   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                          ModelAndView modelAndView) throws Exception
   {
      HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
   }

   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception
   {
      HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
   }
}

   

4.过滤器和拦截器注册AdminWebConfig.java

package cn.togeek.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
 
    /**
     * 配置拦截器
     * @param registry 相当于拦截器的注册中心
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//       下面这句代码相当于添加一个拦截器   添加的拦截器就是我们刚刚创建的
         registry.addInterceptor(new ParamsCheckIntercepter())
//       addPathPatterns()配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
                 .addPathPatterns()
//       excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
//       excludePathPatterns("/","/login")表示放行“/”与“/login”请求
//       如果有静态资源的时候可以在这个地方放行
                 .excludePathPatterns("/","/login");
    }

   @Bean
   public FilterRegistrationBean<RequestParamsFilter> Filters() {
      FilterRegistrationBean<RequestParamsFilter> registrationBean = new FilterRegistrationBean<>();
      registrationBean.setFilter(new RequestParamsFilter());
      registrationBean.addUrlPatterns("/*");
      return registrationBean;
   }
}

5.辅助类RequestInfos.java 对配置进行封装

package cn.togeek.config;

import lombok.Data;

@Data
public class RequestInfos {

   //请求待拦截的url,不带项目名server.servlet.context-path部分
   private String url;

   //待拦截的方法
   private String method;

   //拦截器实现,需继承ParamsCheckInter
   private String fullClassName;

}

6.application.yml配置文件中配置

config:
  intercepters:
    list:
      - {url: /sc/contract, method: PUT}
      - {url: /sc/contract/decomposeway, method: PUT, fullClassName: cn.togeek.config.ScContractDecomposeWayInterHandler}
 

 7.配置类 IntercepterList.java

package cn.togeek.config;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "config.intercepters")
public class IntercepterList {

  private static List<RequestInfos> list;

  public static List<RequestInfos> getList() {
    return list;
  }

  public void setList(List<RequestInfos> list) {
    this.list = list;
  }

}

 

posted @ 2023-05-12 09:38  海的味道  阅读(988)  评论(0编辑  收藏  举报