1.前文提到log4j2日志记录如何使用,目前log4j2的主要作用是记录 请求入参,出参,控制层 ,方法,使用时间等信息.而接口入参是放在spring拦截器中,出参及消耗时间是放在aop的后置通知中.趁着这个机会刚好把当前框架中过滤器,拦截器,aop之间的使用顺序关系梳洗一下.
2.首先来看一下项目中日志记录的内容,主要有发挥作用的有InitInterceptor 拦截其中的preHandle方法(请求进入控制层之前记录时间 控制层 方法 参数 url地址 ),LogAspect中after方法(aop面向切面的后置通知记录返回参数类型参数),InitInterceptor 拦截器中的postHandle方法(在controller方法调用完毕后记录消耗时间及请求完成记录信息),具体相关代码在下面
11:24:16.634 [http-apr-8080-exec-4] INFO com.bxd.core.interceptor.InitInterceptor - -----------------------2019-01-30 11:24:16-------------------------------------
11:28:21.202 [http-apr-8080-exec-3] INFO com.bxd.core.interceptor.InitInterceptor - -----------------------2019-01-30 11:28:21-------------------------------------
Controller: com.bxd.app.controller.sysmgr.SysOrganController$$EnhancerBySpringCGLIB$$2392dc1b
Method : page
Params : {"orgName":[""],"orgNo":[""],"size":["20"],"start":["0"],"page":["1"],"_dc":["1548818901173"]}
URI : /pv_webT/sysmgr/organ/page
11:28:21.235 [http-apr-8080-exec-3] INFO com.bxd.app.aop.LogAspect - com.bxd.core.support.QueryResult@16e6f486
11:28:21.236 [http-apr-8080-exec-3] INFO com.bxd.app.aop.LogAspect - -------------------日志记录结束---------------------
11:28:21.239 [http-apr-8080-exec-3] INFO com.bxd.core.interceptor.InitInterceptor - 耗时 : 37ms
-------------------------------------------------------------------------------
11:28:21.240 [http-apr-8080-exec-3] INFO com.bxd.core.interceptor.InitInterceptor - ********************请求已完成******************
11:28:21.240 [http-apr-8080-exec-3] INFO com.bxd.core.interceptor.InitInterceptor - ********************系统更新完成******************
InitInterceptor 类
public class LogAspect {
private static Logger LOG = LoggerFactory.getLogger(LogAspect.class);
/**
* 在请求进入控制器之前进行拦截,存储请求信息
* @param joinPoint
* @throws JsonProcessingException
*/
public void before(JoinPoint joinPoint) throws JsonProcessingException {
String classType = joinPoint.getTarget().getClass().getName();
if(!"com.bxd.app.controller.APIController".equals(classType)){
return;
}
LOG.info("-------------------开始进行统一日志记录---------------------");
}
/**
* 在controller方法执行完之后进行拦截,存储方法返回的结果信息
* @param joinPoint
* @param returnValue
* @throws JsonProcessingException
*/
public void after(JoinPoint joinPoint,Object returnValue) throws JsonProcessingException {
String classType = joinPoint.getTarget().getClass().getName();
if(StringUtil.hasText("LogController", classType)){
return;
}
if(StringUtil.isNotEmpty(returnValue)){
LOG.info(returnValue.toString());
}
LOG.info("-------------------日志记录结束---------------------");
}
/**
* 把map类型参数转为key,value型参数
* @param map
* @return
*/
protected Map<String, Object> getParams(Map<String, String[]> map) {
Map<String, Object> params = new HashMap<>();
for (Entry<String, String[]> e : map.entrySet()) {
params.put(e.getKey(), e.getValue());
}
return params;
}
LogAspect类
private static final Logger logger = LoggerFactory.getLogger(InitInterceptor.class);
@Autowired
JacksonObjectMapper objectMapper;
/**
* 在执行controller方法之前进行请求参数处理
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod h = (HandlerMethod) handler;
Date dateTime = Calendar.getInstance().getTime();
request.setAttribute("startTime", dateTime.getTime());
if(h.getMethod().getName().contains("refresh")){
logger.info("********************系统缓存更新成功******************************");
return true;
};
Map<String, Object> params = getParams(request);
StringBuilder sb = new StringBuilder(2000);
SimpleDateFormat sfFormat = new SimpleDateFormat("yyyy-MM-dd H:m:s");
sb.append("-----------------------").append(sfFormat.format(dateTime))
.append("-------------------------------------\n");
sb.append("Controller: ").append(h.getBean().getClass().getName()).append("\n");
sb.append("Method : ").append(h.getMethod().getName()).append("\n");
sb.append("Params : ").append(JsonUtil.toJson(params)).append("\n");
sb.append("URI : ").append(request.getRequestURI()).append("\n");
logger.info(sb.toString());
}
return true;
}
/**
* 调用controller方法之后执行日志记录
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
if (handler instanceof HandlerMethod) {
long startTime = (Long) request.getAttribute("startTime");;
Date dateTime = Calendar.getInstance().getTime();
long endTime = dateTime.getTime();
long executeTime = endTime - startTime;
StringBuilder sb = new StringBuilder(1000);
sb.append("耗时 : ").append(executeTime).append("ms").append("\n");
sb.append("-------------------------------------------------------------------------------");
logger.info(sb.toString());
}
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
logger.info("********************请求已完成******************");
if (handler instanceof HandlerMethod){
//HandlerMethod method = (HandlerMethod) handler;
logger.info("********************系统更新完成******************");
}
}
/**
* 把map类型参数转为key,value型参数
* @param map
* @return
*/
protected Map<String, Object> getParams(HttpServletRequest request) {
Map<String, String[]> map = request.getParameterMap();
Map<String, Object> params = new HashMap<>();
for (Entry<String, String[]> e : map.entrySet()) {
params.put(e.getKey(), e.getValue());
}
return params;
}
LogAspect需要配置的spring-aop.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将日志类注入到bean中-->
<bean id="logAsp" class="com.bxd.app.aop.LogAspect"></bean>
<aop:config>
<!--配置在controller包下所有的类在调用之前都会被拦截-->
<aop:pointcut id="logpoint" expression="execution(* com.bxd.app.controller..*(..))"/>
<aop:aspect ref="logAsp" order="2">
<!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="logpoint" method="before"/>
<aop:after-returning pointcut-ref="logpoint" method="after" returning="returnValue"/>
</aop:aspect>
</aop:config>
<!-- 启动对@AspectJ注解的支持 -->
<!--aop:config proxy-target-class="true"></aop:config-->
<!--aop:aspectj-autoproxy proxy-target-class="true"/-->
</beans>
3.从上面我们就可以理清楚拦截器 aop使用顺序是这样的
拦截顺序:filter—>Interceptor—->@Aspect -->Interceptor),具体点可以看下面这张图https://blog.csdn.net/rainbow702/article/details/52185827原文作者地址
https://blog.csdn.net/weixin_39168678/article/details/80987539原文作者地址
4.最后讲一下日志记录中看不到过滤器的作用,过滤器与框架无关主要是配置在web容器中.主要有3个郭过滤器,执行顺序是InitFilter在web容器中并没有做出配置,所以暂不研究.
web.xml配置
<filter> <filter-name>XssFilter</filter-name> <filter-class>com.bxd.core.filter.XssFilter</filter-class> </filter>
XssFilter->XssHttpServletRequestWrapper,通过名字我们就可以知道主要是为了防止xss跨域及跨站攻击的.
XssFilter.class
public class XssFilter implements Filter {
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XssHttpServletRequestWrapper(
(HttpServletRequest) request), response);
}
}
XssHttpServletRequestWrapper.class
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
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] = cleanXSS(values[i]);
}
return encodedValues;
}
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null)
return null;
return cleanXSS(value);
}
private String cleanXSS(String value) {
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
return value;
}
}
最后:项目中对于权限的控制 只是在前端做了控制,后端 只是针对用户是否登录做了校验,并没有对权限问题作出解决.最近想着能否利用拦截器 也就是preHandler 中对权限进行控制