java使用自定义注解+SpringMVC+拦截器实现简单的用户操作日志记录

1。自定义注解

@Documented //支持JavaDoc文档注释

@Inherited // 表某个被标注的类型是被继承的

@Target(ElementType.METHOD) //表明该注解对成员方法起作用

@Retention(RetentionPolicy.RUNTIME) //在编译以后仍然起作用

public @interface logRecord {

/**

* @Description: 是否记录日志

* @return: String

* @author: fss

*/

String validate() default AccessLoggingCode.CHECK_API;

/**

* @Description: 操作类型

* @return: String

* @author: fss

*/

String operation() default AccessLoggingCode.SELECT;

/**

* @Description: 操作模块

* @return: String

* @author: fss

*/

String operationModule() default "默认操作模块";

/**

* @Description: 操作备注

* @return: String

* @author: fss

*/

String remark() default "默认备注";

/**

* @Description: 操作平台

* @return: String

* @author: fss

*/

String operationTerrace() default AccessLoggingCode.savingAPP;

}


2。controller

/**

*

* @Title: SupplierLogin 

* @Description: app端运营商登录

* @param: @param response

* @param: @param request

* @param: @return     

* @return: Map<String,Object>

* @author: FSS   

* @throws

*/

@ResponseBody

@DataSourceAuth(validate=DatasourceConfig.dataSourceA)

@PowerAuth(validate=RestPowerAPIcontent.NORMAL_API)

@logRecord(operation=AccessLoggingCode.LOGIN ,operationTerrace = AccessLoggingCode.operatorAPP , operationModule = "app端运营商登录" , remark =AccessLoggingCode.LOGIN)

@RequestMapping(value="/v1/Amd/OperatorLogin/login",method ={RequestMethod.POST},produces="application/json;charset=utf-8")

public Map<String,Object> userLoginaes(@RequestBody Map<String,String> map){

return operatorMemberService.operatorLoginingApp(map);

}

3.在spring.xml配置文件来构建拦截器(xxxx包名)

<!-- 客户端访问日志拦截器 -->

  <mvc:interceptor>

    <mvc:mapping path="/app/**" />

<mvc:mapping path="/rest/**" />

<mvc:mapping path="/test/**" />

<bean class="com.xxxx.filter.logRecordInterceptor"></bean>

  </mvc:interceptor>

4.创建上面配置的拦截器, 来继承HandlerInteceptorAdaptor 或者实现 HandlerInteceptor 接口.这里集成 HandlerInteceptorAdaptor



public class logRecordInterceptor extends HandlerInterceptorAdapter {

@Autowired

private UserAccessLoggingDao useraccessLoggingDao;

/**

* 在拦截器中通过ContentCachingResponseWrapper获取HttpServletResponse响应体中的数据

* @param response

* @return

* @throws IOException

* @author: fss

*/

public static Map<String, Object> readPostJsonData(HttpServletResponse response) throws IOException {

        if (response instanceof ContentCachingResponseWrapper) {

        /*response.setCharacterEncoding("UTF-8"); 

    response.setHeader("Content-Type", "text/html;charset=UTF-8");*/

        ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response;

            int contentLenth = ((ContentCachingResponseWrapper) response).getContentSize();

            if (contentLenth <= 0) {

                return null;

            }

            byte[] bytes = new byte[contentLenth];

            InputStream is = responseWrapper.getContentInputStream();

            for (int index = 0; index < contentLenth; index++) {

                int value = is.read();

                if (value == -1) {

//                is.reset();

                    break;

                }

                bytes[index] = (byte) value;

            }

            //responseWrapper.getContentAsByteArray();//这里是为了测试

            String data = new String(bytes);

            //System.out.println("http util .........data:" + data);

            Map<String, Object> map = MapUtils.stringToMap(data);

            return map;

        }

        return null;

    }

@Override

public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {

return super.preHandle(request, response, handler);

}

// \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔),

// 字符串在编译时会被转码一次,所以是 "\\b"

// \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔)

static String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i" + "|windows (phone|ce)|blackberry" + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp"

+ "|laystation portable)|nokia|fennec|htc[-_]" + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";

static String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser" + "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b";

// 移动设备正则匹配:手机端、平板

static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE);

static Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE);

/**

* 获取设备名称

* @param userAgent

* @return

*/

protected String getDeviceName(String userAgent) {

if (null == userAgent) {

userAgent = "";

}

// 匹配

Matcher matcherPhone = phonePat.matcher(userAgent);

Matcher matcherTable = tablePat.matcher(userAgent);

if (matcherPhone.find()) {

return matcherPhone.group();

} else if (matcherTable.find()) {

return matcherTable.group();

} else {

return "pc";

}

}

/**

* @Description: 返回处理

* @return: String

* @author: fss

*/

@SuppressWarnings("unchecked")

public void postHandle(HttpServletRequest request,

HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

HandlerMethod hm =  (HandlerMethod) handler;

logRecord record = hm.getMethodAnnotation(logRecord.class);

if (record != null && record.validate().equals(AccessLoggingCode.CHECK_API)) {

  String operation = record.operation();//操作类型

  String businessLogic = record.operationModule();//操作模块

  String remark = record.remark();//备注

  String terrace = record.operationTerrace();//操作平台

  String url = request.getRequestURI();// 操作的URI

  String ip = CusAccessObjectUtil.getIpAddr(request);//访问ip

  String memberId="";

  String Agent = request.getHeader("User-Agent");//DEProvinceFuBao/1.89 (iPhone; iOS 12.2; Scale/3.00)

  String getDeviceName = getDeviceName(Agent);

  try {

    //取用户信息

    if(terrace.equals(AccessLoggingCode.savingAPP)||terrace.equals(AccessLoggingCode.supplierAPP)){

  String memberId1=request.getHeader("memberId");

  if(memberId1!=null&&!operation.equals(AccessLoggingCode.LOGIN)){//非登录从Header取用户信息

  memberId = memberId1;

  }else{ //从response取用户信息

  Map<String, Object> parametersAll = readPostJsonData(response);

  Map<String, Object> parameters = null;

  if(!MapUtils.mapIsAnyBlank(parametersAll, "data")){

  parameters =  (Map<String, Object>) parametersAll.get("data");

  if(!MapUtils.mapIsAnyBlank(parameters, "memberId")){

  memberId = parameters.get("memberId").toString();

  remark = "请求成功";

  }

  }else if(MapUtils.mapIsAnyBlank(parametersAll, "data")&&!MapUtils.mapIsAnyBlank(parametersAll, "code")&&!SuccessCodeConfigure.SUCCESS_CODE.equals(parametersAll.get("code"))){//访问失败

      String  errorMessage = "请求失败";

      if(!MapUtils.mapIsAnyBlank(parametersAll, "errorMessage")){

  errorMessage = parametersAll.get("errorMessage").toString();

  }

  remark = "errorMessage:"+errorMessage;

  }

  }

}else if(terrace.equals(AccessLoggingCode.operatorAPP)){

String cOperatorId=request.getHeader("cOperatorId");

if(cOperatorId!=null&&!operation.equals(AccessLoggingCode.LOGIN)){//非登录从Header取用户信息

memberId = cOperatorId;

}else{//从response取用户信息

  Map<String, Object> parametersAll = readPostJsonData(response);

  Map<String, Object> parameters = null;

  if(!MapUtils.mapIsAnyBlank(parametersAll, "data")){

parameters =  (Map<String, Object>) parametersAll.get("data");

if(operation.equals(AccessLoggingCode.LOGIN)||operation.equals(AccessLoggingCode.LOGOUT)){

if(!MapUtils.mapIsAnyBlank(parameters, "cOperatorId")){

    memberId = parameters.get("cOperatorId").toString();

}

}

  }else if(!MapUtils.mapIsAnyBlank(parametersAll, "code")&&!SuccessCodeConfigure.SUCCESS_CODE.equals(parametersAll.get("code"))){//访问失败

      String  errorMessage = "请求失败";

  if(!MapUtils.mapIsAnyBlank(parametersAll, "errorMessage")){

  errorMessage = parametersAll.get("errorMessage").toString();

  }

  remark = "errorMessage:"+errorMessage;

      }

    }

}

} catch (Exception e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

//执行日志记录(此处根据自己业务逻辑写持久化代码)


}

super.postHandle(request, response, handler, modelAndView);

}

/**

* 从request中获得参数Map,并返回可读的Map

*

* @param request

* @return

*/

@SuppressWarnings({ "unchecked", "rawtypes" })

public static Map<String,Object> getParameterMap(HttpServletRequest request) {

    // 参数Map

    Map<String,Object> properties = request.getParameterMap();

    // 返回值Map

    Map<String,Object> returnMap = new HashMap();

    Iterator entries = properties.entrySet().iterator();

    Map.Entry entry;

    String name = "";

    String value = "";

    while (entries.hasNext()) {

        entry = (Map.Entry) entries.next();

        name = (String) entry.getKey();

        Object valueObj = entry.getValue();

        if(null == valueObj){

            value = "";

        }else if(valueObj instanceof String[]){

            String[] values = (String[])valueObj;

            for(int i=0;i<values.length;i++){

                value = values[i] + ",";

            }

            value = value.substring(0, value.length()-1);

        }else{

            value = valueObj.toString();

        }

        returnMap.put(name, value);

    }

    return returnMap;

}

}



其实利用拦截器还可以做简单的权限控制、一些敏感词过滤、简单的数据统计

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容