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请求参数。

AOP日志组件 多次获取post参数的更多相关文章

  1. Performance Counter的使用——获取各类组件性能,获取CPU参数等

    一 PerformanceCounter 基本介绍1 简单介绍表示 Windows NT 性能计数器组件 命名空间:System.Diagnostics程序集:System(在 system.dll ...

  2. 08 SSM整合案例(企业权限管理系统):11.AOP日志

    04.AdminLTE的基本介绍 05.SSM整合案例的基本介绍 06.产品操作 07.订单操作 08.权限控制 09.用户和角色操作 10.权限关联 11.AOP日志 11.AOP日志 1.数据库与 ...

  3. vue-router2.0 组件之间传参及获取动态参数

    <li v-for=" el in hotLins" > <router-link :to="{path:'details',query: {id:el ...

  4. [js开源组件开发]query组件,获取url参数和form表单json格式

    query组件,获取url参数和form表单json格式 距离上次的组件[js开源组件开发]ajax分页组件一转眼过去了近二十天,或许我一周一组件的承诺有了质疑声,但其实我一直在做,只是没人看到……, ...

  5. 我心中的核心组件(可插拔的AOP)~第十五回 我的日志组件Logger.Core(策略,模版方法,工厂,单例等模式的使用)

    回到目录 之前的讲过两篇关于日志组件的文章,分别是<第一回  日志记录组件之自主的Vlog>和<第三回  日志记录组件之log4net>,而今天主要说一下我自己开发的另一种日志 ...

  6. java日志组件介绍(common-logging,log4j,slf4j,logback )

    转自:http://www.blogjava.net/daiyongzhi/archive/2014/04/13/412364.html common-logging是apache提供的一个通用的日志 ...

  7. 你的日志组件记录够清晰嘛?--自己开发日志组件 Logger

    现在现成的日志组件实在是太多太多,为什么我还需要自己实现呢????? 需求来源于java的log4j, [07-31 16:40:00:557:WARN : com.game.engine.threa ...

  8. 转:java日志组件介绍(common-logging,log4j,slf4j,logback )

    原网址:http://www.blogjava.net/daiyongzhi/archive/2014/04/13/412364.html common-logging common-logging是 ...

  9. 日志组件logback的介绍及配置使用方法

    一.logback的介绍 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback-core,logback- classic和logback-acc ...

随机推荐

  1. unity从模型中抽取动画文件(animation)

    http://www.cnblogs.com/leng-yuye/archive/2013/01/11/2856144.html 由于模型是由第三方的软件制作的,用unity不能直接编辑模型里的动画文 ...

  2. c#实现优先级队列

    http://www.baidu.com/s?wd=c%23%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97&ie=utf-8&f=8& ...

  3. [Xcode 实际操作]一、博主领进门-(6)Xcode的iOS模拟器的基本使用方法

    目录:[Swift]Xcode实际操作 本文将演示Xcode的iOS模拟器的基本使用方法. 在项目导航区,鼠标右键[Assets.xcassets]资源文件夹. 隔壁右侧区域左下角点击[+],打开资源 ...

  4. Suse 11 sp4 安装Oracle 11g

    环境信息 suse 11 sp4 64位 企业版配置:虚拟机,1U4CORE,4GB内存,100GB存储(实际可用空间20GB) linux环境配置修改 在安装oracle之前,需要先对linux 的 ...

  5. hard(2018.10.18)

    题意:给你一棵\(n\)个节点的树,\(q\)个询问,每次询问读入\(u,v,k,op\),需要满足树上有\(k\)对点的简单路径交都等于\(u,v\)之间的简单路径,\(op=1\)表示\(k\)对 ...

  6. mongodb数据库下载链接,相关配置(转载),官方api

    下载链接:http://dl.mongodb.org/dl/win32/x86_64 配置:http://blog.sina.com.cn/s/blog_685213e70101g81t.html 官 ...

  7. JMETER通过java代码通过代码/ JMETER API实现示例进行负载测试

    本教程试图解释Jmeter的基本设计,功能和用法,Jmeter是用于在应用程序上执行负载测试的优秀工具.通过使用jmeter GUI,我们可以根据我们的要求为请求创建测试样本并执行具有多个用户负载的样 ...

  8. Codeforces 1137B(kmp next数组构造)

    为了物尽其用,Next求出最多有哪部分能重复使用,然后重复使用就行了-- const int maxn = 5e5 + 5; char s[maxn], t[maxn]; int cnts0, cnt ...

  9. Influxdb 时序数据库 windows 安装

    Influxdb 是一款比较火爆的时序数据库,本文介绍如何在 windows 平台下安装. 1.场景: windows 平台的 influxdb 似乎只支持单机非windows 服务的安装方式 适用于 ...

  10. linux系统任务调度命令crontab

    循环重复的执行计划任务.有计划性的执行任务,像这种任务,在linux系统中就有cron命令来完成. linux系统下的任务调度分为两类:系统任务调度和用户任务调度. /etc/crontab文件就是系 ...