AOP日誌組件 屢次獲取post參數

AOP日誌組件 屢次獲取post參數

  • 需求:新增接口日誌組件。經過攔截器對接口URL進行攔截處理,而後將接口post請求的參數與結果,寫入日誌表。
  • 問題:POST方法的參數是存儲在request.getInputStream中,只能讀一次,不能屢次讀取。從中讀取post請求參數,只能讀取一次。在filter中獲取以後,controller沒法獲取post請求參數。
  • 解決辦法:繼承HttpServletRequest,先獲取到請求參數,再加參數從新set到inputStream。

1.攔截器記錄日誌

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        String requestPath  = httpRequest.getServletPath().split("\\?")[0];

        if(request  == null){
            chain.doFilter(request, response);
            return ;
        }
         for (String noFilter : NO_FILETER_LIST) {
             if(requestPath.toLowerCase().contains(noFilter)){
                 chain.doFilter(request, response);
                 return ;
             }
         }

         Pattern pattern = Pattern.compile(".*[api|interface].*");
         Matcher matcher = pattern.matcher(requestPath);
         if(matcher.matches()){
             LoggerManager loggerManager = SpringLoadListener.getBean("loggerManager",LoggerManager.class);
             //擴展request,防止字符注入等
             XssSqlHttpServletRequestWrapper xssRequest = new XssSqlHttpServletRequestWrapper((HttpServletRequest)request);
             ResponseWrapper wrapResponse = new ResponseWrapper((HttpServletResponse)response);
             //繼續請求處理類
             chain.doFilter(xssRequest, wrapResponse);
             //獲取URL的參數 get請求
             Map<Object, Object> urlParam = xssRequest.getParameterMap();
             //獲取post參數
             String param = xssRequest.getRequestParams();
             JSONObject jObject = JSONObject.fromObject(urlParam);
             jObject.put("postParam", param.toString());
             String params = jObject.toString();
             //獲取接口處理類返回結果
             byte[] data = wrapResponse.getResponseData();
              String result = new String(data,"UTF-8");
              //保存日誌
              loggerManager.operateInterfaceLogger("", params, requestPath, result);
              ServletOutputStream out = response.getOutputStream();
              out.write(data);
              out.flush();
         }else{
             chain.doFilter(request, response);
         }

    }

2.處理request請求,先獲取inputStream,將請求參數複製給其餘方法,同時再將獲取的inputStream賦值回request中。

public class XssSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
         HttpServletRequest orgRequest = null;
         private byte[] bytes;
         private WrappedServletInputStream  wrappedServletInputStream;

   public XssSqlHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
           super(request);
           this.orgRequest = request;
           //讀取輸入流的請求參數,保存到bytes中
           bytes = IOUtils.toByteArray(request.getInputStream());
           ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
           this.wrappedServletInputStream = new WrappedServletInputStream(byteArrayInputStream);

           //把post參數從新寫入請求流
           reWriteInputStream();
   }

   /** * 把參數從新寫進請求裏 */
    public void reWriteInputStream() {
       wrappedServletInputStream.setStream(new ByteArrayInputStream(bytes != null ? bytes : new byte[0]));
   }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return wrappedServletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(wrappedServletInputStream));
    }

    /** * 獲取post參數 */
    public String getRequestParams() throws IOException {
        return new String(bytes, this.getCharacterEncoding());
    }

   //清洗參數,防止xss注入
   public String[] getParameterValues(String parameter)
   {
           String[] values = super.getParameterValues(parameter);
           if (values == null) {
             return null;
     }
           int count = values.length;
           String[] encodedValues = new String[count];
           for (int i = 0; i < count; i++) {
             encodedValues[i] = xssEncode(values[i]);
     }
           return encodedValues;
   }


   public String getParameter(String name)
   {
           String value = super.getParameter(xssEncode(name));
           if (value != null) {
             value = xssEncode(value);
     }
           return value;
   }

   public String getHeader(String name)
   {
           String value = super.getHeader(xssEncode(name));
           if (value != null) {
             value = xssEncode(value);
     }
           return value;
   }


   private static String xssEncode(String s)
   {
           return StringUtil.xssEncode(s);
   }





   public HttpServletRequest getOrgRequest()
   {
           return this.orgRequest;
   }


   public static HttpServletRequest getOrgRequest(HttpServletRequest req)
   {
           if ((req instanceof XssSqlHttpServletRequestWrapper)) {
             return ((XssSqlHttpServletRequestWrapper)req).getOrgRequest();
     }

         return req;
   }


   private class WrappedServletInputStream extends ServletInputStream {
       public void setStream(InputStream stream) {
           this.stream = stream;
       }
       private InputStream stream;
       public WrappedServletInputStream(InputStream stream) {
           this.stream = stream;
       }
       public int read() throws IOException {
           return stream.read();
       }

       public boolean isFinished() {
           return true;
       }

       public boolean isReady() {
           return true;
       }

       public void setReadListener(ReadListener readListener) {

       }
   }

 }

3.Manager保存日誌記錄。對LoggerAnnotation自定義註解,實現AOP日誌保存。

@LoggerAnnotation(type="INTERFACE", name="#name", params="#params", desc="#pageLink")
public String operateInterfaceLogger(String name, String params, String pageLink, String result)
{
    return result;

}


 @Component
 @Aspect
 @Async
 public class LoggerOperationAop
 {
   private static final Logger logger = LoggerFactory.getLogger(LoggerOperationAop.class);
   @Autowired
   private LoggerManager loggerManager;
   @Autowired
   private UserManager userManager;
   @Autowired
   private JdbcTemplateImpl jdbcTemplate;

   public LoggerOperationAop() {}

   @Pointcut("@annotation(LoggerAnnotation)")
   private void serviceAspect(LoggerAnnotation LoggerAnnotation) {}

   @Around("serviceAspect(LoggerAnnotation)")
   public Object process(ProceedingJoinPoint point, LoggerAnnotation LoggerAnnotation)
     throws Throwable
   {
     String targetName = point.getTarget().getClass().getName();
     String methodName = point.getSignature().getName();
     Object[] arguments = point.getArgs();

     String operationType = LoggerAnnotation.type();
     String message = LoggerAnnotation.desc();
     String params = LoggerAnnotation.params();
     String name = LoggerAnnotation.name();
     String id = LoggerAnnotation.id();
     String[] paramNames = ReflectParamNames.getNames(targetName, methodName);
     if ((StringUtil.isNotBlank(name)) && (name.startsWith("#"))) {
       String value = SpelParser.getKey(name, "", paramNames, arguments);
       if (StringUtil.isNotBlank(value)) {
         targetName = value;
       }
     }
     String methodParams = "";
     if ((StringUtil.isNotBlank(params)) && (params.startsWith("#"))) {
       methodParams = SpelParser.getKey(params, "", paramNames, arguments);
     }
     if ((StringUtil.isNotBlank(message)) && (message.startsWith("#"))) {
       String value = SpelParser.getKey(message, "", paramNames, arguments);
       if (StringUtil.isNotBlank(value)) {
         message = value;
       }
     }

     String userId = null;
     String userName = "遊客";
     try {
         Subject currentSubject = SecurityUtils.getSubject();
         if (currentSubject != null) {
           Object tmpObj = currentSubject.getSession().getAttribute("authorizeUser");
           if ((tmpObj != null) && ("INTERFACE".equals(operationType))) {
             userName = StringUtil.parseAny2String(tmpObj);
           } else if (currentSubject.getPrincipal() != null) {
             ShiroDbRealm.ShiroUser shiroUser = (ShiroDbRealm.ShiroUser)currentSubject.getPrincipal();
             userId = shiroUser.getUserId();
             userName = shiroUser.getLoginName();
           }
         }
       } catch (Exception e) {
         logger.info("不支持shiro技術框架");
         logger.error("異常信息:{}", e.getMessage());
       }

     Object target = point.proceed();
     try
     {
       String extName = DateUtil.getYearMonth(new Date());
       String sql = " INSERT INTO T_E4S_DB_LOG_MESSAGE_" + extName + 
         " (USER_ID,USER_NAME,CLASS_NAME,METHOD_NAME,METHOD_PARAMS,LOG_DATE,LOG_MESSAGE,OPERATION_TYPE,REMARK) " + 
         " VALUES(?,?,?,?,?,?,?,?,?) ";
       Object[] object = { userId, userName, targetName, methodName, methodParams, 
         new Date(), message, operationType, target };
       this.jdbcTemplate.beginTranstaion();
       this.jdbcTemplate.update(sql, object);
       this.jdbcTemplate.commit();
     }
     catch (Exception ex) {
       logger.error("==異常通知異常==");
       logger.error("異常信息:{}", ex.getMessage());
       this.jdbcTemplate.rollback();
     }

     return target;
   }
 }

LoggerFilter裏一個實現了XssSqlHttpServletRequestWrapper類,其構造器自動讀取了servletRequest裏的輸入流,並把數據保存了下來,最後又把數據從新寫入servletRequest裏,在filter中能夠讀取post請求參數,cotroller也能夠再次從request裏讀取到post請求參數。java

相關文章
相關標籤/搜索