aop实现记录后端用户访问

记录:

  访问ip, 请求链接, 请求参数, 请求头, 返回信息...

 

问题1:

  如何实现aop切片的就是Controller中的请求方法?

    监听xxxController下的所有方法?

      担心Controller中有部分方法是普通方法,例如private String datePares(Date date)这种

    使用过滤器?

      此业务不应该用过滤器

    使用拦截器?

      要求参照前面aop的例子完成,所以先不考虑。

  解决方案:

   spring的切点定义允许通过注解定位,结合execution表达式完成切点。

  @Pointcut(" @annotation(org.springframework.web.bind.annotation.PostMapping)"
      + " || @annotation(org.springframework.web.bind.annotation.PutMapping)"
      + " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
      + " || @annotation(org.springframework.web.bind.annotation.RequestMapping)")
  private void requestAspect() {
  }

  @Pointcut("execution(* com.duoyu.home..* (..))")
  private void backendAspect() {

  }

  @Around("requestAspect() && backendAspect()")
  public Object run(ProceedingJoinPoint point) throws Throwable {

 

问题2:

  处理方法中如何得到HttpServletRequest?

  spring提供了RequestContextListener这个监听器,针对请求,完成了一份数据的拷贝。(我使用的是boot,应该是直接自动配置了)

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

 

 问题3:

  获得请求ip?请求方式?url?header头信息?请求参数?body体信息?

  获取请求ip前面想直接用request.getRemoteHost()获得,纠结remoteHost()和remoteAddr()有什么区别,于是上网找了个获取的方法,顺便简单针对被代理了的ip的追踪

public static String getClientIp(HttpServletRequest request) {
    //反向代理了的话
    String ip = request.getHeader("x-forwarded-for");
    //多级反向代理处理
    if (ip != null && ip.contains(",")) {
      for (String ipItem : ip.split(",")) {
        if (!ipItem.equals("unknown")) {
          ip = ipItem;
          break;
        }
      }
    }
    //如果x-forwarded-for获取不到,或者是unknown,依旧使用了反向代理
    if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getHeader("WL-Proxy-Client-IP");
    }

    //没有使用反向代理
    if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
    }

    return ip;
  }

  请求uri 和请求method很简单

  请求header:

    Map<String, String> headerMaps = new HashMap<String, String>();
    Enumeration headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
      String key = (String) headerNames.nextElement();
      String value = request.getHeader(key);
      headerMaps.put(key, value);
    }
    String headers = JSONObject.toJSONString(headerMaps);
    visitRecordDo.setHeaders(headers);

  因为http请求的参数可能在body体内,也可能不在body体内,所以针对请求参数使用2种方式获得:

  1、  request.getParameterMaps() 然后遍历获取

  2、       request.getInputStream()读取流信息获取body数据

 

  响应信息,直接执行切点对象即可。

 Object resultObj = point.proceed();

 

 

问题4:

  问题4是一个大问题,前面编写完毕后,简单使用看上去没问题。但是在我某些post操作时候后台日志报错:流已被关闭。

  为什么有的请求读取request的流不会被关闭有些被关闭呢?

    我发现出现流被关闭的都是使用了@RequestBody的注解的方法上。@RequestBody会读取request的body体内容且转化为对应对象。且断点发现@RequestBody注入的对象里面有值。

    也就是说,先进入的方法头,读取掉了request内容,然后再被aop切面,发现流关闭报错。

 

  怎么解决这个问题?

    拦截器是基于AOP实现的,好像只有过滤器能够优先执行。

    网上解决方案:

      过滤器拿到请求,读取请求信息存储到byte[]数组,然后返回一个包装的装载数组数据的request给下游。

      如:https://blog.csdn.net/a704397849/article/details/97267572

    我这里业务不应该使用过滤器,应该稍微变通了下我的需求,只要参数即可,那么我不区分其他参数和body体参数。使用切点的获取参数方法直接获取参数列表

    String params = JSONObject.toJSONString(point.getArgs());

 

 

 

posted @ 2020-07-22 19:07  guodaxia  阅读(372)  评论(0编辑  收藏  举报