问题过程
安全改造过程中,将操作日志的方法抽象成一个公共jar包,单元测试通过,上了生产也看到有日志正常输出。但第二个系统接入上生产前,测试反馈请求报500,后台有错误日志:
java.lang.NoSuchMethodError: org/apache/commons/lang/StringUtils.startsWithIgnoreCase(Ljava/lang/String;Ljava/lang/String;)Z
at com.basicSupport.common.util.LogUtil.sensitiveOp(LogUtil.java:79)
at com.basicSupport.common.util.LogUtil.sensitiveOp(LogUtil.java:55)
at com.paic.sales.train.web.controller.TrainController.queryVisitPlan(TrainController.java:136)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:301)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at com.paic.sales.support.filter.DmzToWebFilter.doFilter(DmzToWebFilter.java:31)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at com.paic.sales.support.filter.EHomeLogonFilter.doFilter(EHomeLogonFilter.java:85)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at com.paic.sales.support.filter.URLThreadControlFilter.doFilter(URLThreadControlFilter.java:38)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at com.basicSupport.security.filter.BaseLimitRequestFilter.doFilter(BaseLimitRequestFilter.java:81)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at com.pingan.pss.protector.ip.IpFilter.doFilter(IpFilter.java:55)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:60)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3748)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3714)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2283)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2182)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1499)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:263)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)
日志指向很明显,StringUtils.startsWithIgnoreCase方法不存在,看common-lang包,2.0确实没有这个方法,升级为2.4后,恢复正常。
复盘
虽然解决,但仍有疑问:
1、自测过程日志正常输出,测试环境也有日志打印出来。
2、已接入的系统common-lang也是2.0,生产日志也是有输出的。
首先,本地也单元测试尝试复现,为保持jar包版本一致,在出错的版本中写单测
@Test
public void queryCustomerListTest() {
try {
request = this.getGetRequest("eHomeLogin.login.shtml5");
sensitiveOp("", "queryCustomerList",request, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
15:22:26 Tests Passed: 1 passed
通过没有报错,进入单步调试代码
public static void sensitiveOp(String empNo, String requestType, HttpServletRequest request, Integer operateResult, String operationType) {
try {
if(securityLogger.isTraceEnabled()) {
String ignored = DateUtils.formatDate(new Date(), "yyyy-MM-dd hh:mm:ss");
String url = request.getRequestURI();
String ip = CommonUtil.getRemoteHost(request);
Map parameterMap = request.getParameterMap();
HashMap info = new HashMap();
info.put("App_id", BasicSupportPropertiesUtil.getPropertyValues("sales.subsystem.name"));
info.put("Company_code", "PA002");
info.put("Request_url", url);
info.put("Operation_result", "Success");
info.put("Operation_user", empNo);
info.put("Record_counts", operateResult);
info.put("Server_ip", request.getLocalAddr());
info.put("Operation_type", operationType);
info.put("Request_type", requestType);
info.put("Src_ip", ip);
info.put("Operation_time", ignored);
info.put("Operation_user_type", !StringUtils.isNumeric(empNo) && !StringUtils.startsWithIgnoreCase(empNo, "xn")?"2":"1");
info.put("Operation_object", parameterMap.keySet());
securityLogger.trace(GSON.toJson(info));
}
} catch (Excetion var10) {
;
}
}
多次调试,突然想到isNumeric方法是存在的,表达式遵循左匹配原则,!isNumeric为false,startsWithIgnoreCase方法不会执行,所以有成功的情况。
info.put("Operation_user_type", !StringUtils.isNumeric(empNo) && !StringUtils.startsWithIgnoreCase(empNo, "xn")?"2":"1");
更改方法第一个参数为“aaaa”,单测failed,重现成功。
然而当初为了避免方法内异常影响到功能,特意catch了exception,但还是影响了正常功能,应该用Throwable,重新打包,执行单测,测试案例通过了,方法内的error不会影响原功能了。
catch (Throwable var10) {
;
}
总结
1、jar包功能单独测试的时候,案例执行覆盖全了,不代表被引用到别的系统也是正常的,会因为第三方jar包版本不一致报错。要谨慎对待每一次变更。
2、公共jar包方法为避免错误扩散,仅仅catch Exception 是不够的,应该用catch Throwable 。