最近系统在做安全测评,要求所有数据操作都要保留操作痕迹,于是PM要求所有接口按照增加、修改、查询、删除、导出、导入、审批、文件上传下载删除这几大操作类型统一增加日志。这一需求的典型实现方法是给Controller层的接口加上自定义注解,通过AOP拦截所有方法来实现。我实现的这个自定义注解传入的参数有menu菜单名、title操作数据说明及操作类型,记录格式一般是:用户名+操作类型+菜单+操作数据说明+获取的参数信息。下面简单说一下其中的逻辑。
- 1.增加:单条新增数据的方法统一返回新增记录id,根据拦截到的方法返回值获取id记录
- 2.删除:循环request中的参数获取到id并记录
Enumeration enu=request.getParameterNames();
while(enu.hasMoreElements()){
String paraName=(String)enu.nextElement();
Object paramVal1 = request.getParameter(paraName);
if(paramVal1 != null && !"".equals(paramVal1.toString())){
sb.append(paraName+"为"+paramVal1+";");
}}
- 3.查询:一般是GET请求也是循环request中的参数获取查询条件,这里注意参数有的放在request里,有的是如queryXXX/id 这种直接放在请求路径上的
String paraName=(String)enu.nextElement();
// 解析查询路径传参的情况,如 url/id
if ("_".equals(paraName)){
Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String idInPath = (String) pathVariables.get("id");
if (idInPath != null) {
sb.append("id为"+idInPath+";");
}
}
- 4.修改/导出操作:默认也是根据request获取信息,但是我的POST请求类型是通过joinPoint.getArgs()获取的
StringBuffer exportStr = new StringBuffer();
String title = operLog.getTitle();
// 统计报表专用(因为所有统计报表公用一个导出接口)
Object[] args = joinPoint.getArgs();
if ("统计报表".equals(controllerLog.menu()) && args.length>0){
Map arguMap = (Map) args[0];
title = arguMap.get("reportName")+"穿透";
exportStr.append("tn为").append(arguMap.get("tn"))
.append("exportMode为").append(arguMap.get("exportMode"));
}
- 5.文件上传、下载、删除
我的文件上传是通过WebUploader组件实现的,上传文件记录了文件名,而下载和删除文件都只记录了文件id。注意这里有个小坑,Controller中获取文件名是直接将HttpServletRequest请求映射为MultipartHttpRequest实现的:
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
通过反射获取到request之后再这样写就报错:cannot be cast to org.springframeworkweb.multipart.MultipartHttpServletRequest
于是查了一下发现需要通过MultipartResolver转一下:
MultipartResolver resolver = new StandardServletMultipartResolver();
MultipartHttpServletRequest multiRequest = resolver.resolveMultipart(request);
这个具体原理大家可以查询,其他方式实现的文件上传不一定适用此方法。感兴趣的朋友可以留言跟我要相关代码及数据表。