基于博客系统的访客日志记录----代码合集
本文章是基于我的另一篇博客所写的相关代码,如果还没看过的可以先看看我这篇文章:
https://blog.csdn.net/qq_56769991/article/details/123915587
核心代码
自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisitLogger {/*** 访问行为枚举*/VisitBehavior value() default VisitBehavior.UNKNOWN;
}
切面配置:
@Component
@Aspect //该类用作切面类
public class VisitLogAspect {@AutowiredVisitLogService visitLogService;@AutowiredVisitorService visitorService;@AutowiredRedisService redisService;ThreadLocal<Long> currentTime = new ThreadLocal<>();/*** 配置切入点:表示被VisitLogger注解的方法都可以设置为切入点。* 下面的没有内容的方法,其实就是代表被视为切入点的那个方法,在形参中我们传入了参数visitLogger,因为我们要使用被* 切入点方法注解中的相关参数*/@Pointcut("@annotation(visitLogger)")public void logPointcut(VisitLogger visitLogger) {}/*** 配置环绕通知** @param joinPoint* @return* @throws Throwable* 在切入点的前后进行操作* 这里的ProceedingJoinPoint表示被切入点的那个方法里面的逻辑内容* visitLogger表示我们注解的内容* 这些参数系统会帮我们自动传入* 在环绕通知的返回值应该与被环绕的方法的返回值一致*/@Around("logPointcut(visitLogger)")public Object logAround(ProceedingJoinPoint joinPoint, VisitLogger visitLogger) throws Throwable {currentTime.set(System.currentTimeMillis());Result result = (Result) joinPoint.proceed();int times = (int) (System.currentTimeMillis() - currentTime.get());currentTime.remove();//获取请求对象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//校验访客标识码String identification = checkIdentification(request);//记录访问日志VisitLog visitLog = handleLog(joinPoint, visitLogger, request, result, times, identification);//存到数据库visitLogService.saveVisitLog(visitLog);return result;}/*** 校验访客标识码** @param request* @return*/private String checkIdentification(HttpServletRequest request) {String identification = request.getHeader("identification");if (identification == null) {//请求头没有uuid,签发uuid并保存到数据库和Redisidentification = saveUUID(request);} else {//校验Redis中是否存在uuidboolean redisHas = redisService.hasValueInSet(RedisKeyConstants.IDENTIFICATION_SET, identification);//Redis中不存在uuidif (!redisHas) {//校验数据库中是否存在uuidboolean mysqlHas = visitorService.hasUUID(identification);if (mysqlHas) {//数据库存在,保存至RedisredisService.saveValueToSet(RedisKeyConstants.IDENTIFICATION_SET, identification);} else {//数据库不存在,签发新的uuididentification = saveUUID(request);}}}return identification;}/*** 签发UUID,并保存至数据库和Redis** @param request* @return*/private String saveUUID(HttpServletRequest request) {//获取响应对象HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();//获取当前时间戳,精确到小时,防刷访客数据Calendar calendar = Calendar.getInstance();calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);String timestamp = Long.toString(calendar.getTimeInMillis() / 1000);//获取访问者基本信息String ip = IpAddressUtils.getIpAddress(request);String userAgent = request.getHeader("User-Agent");//根据时间戳、ip、userAgent生成UUIDString nameUUID = timestamp + ip + userAgent;String uuid = UUID.nameUUIDFromBytes(nameUUID.getBytes()).toString();//添加访客标识码UUID至响应头response.addHeader("identification", uuid);//暴露自定义header供页面资源使用response.addHeader("Access-Control-Expose-Headers", "identification");//校验Redis中是否存在uuidboolean redisHas = redisService.hasValueInSet(RedisKeyConstants.IDENTIFICATION_SET, uuid);if (!redisHas) {//保存至RedisredisService.saveValueToSet(RedisKeyConstants.IDENTIFICATION_SET, uuid);//保存至数据库Visitor visitor = new Visitor(uuid, ip, userAgent);visitorService.saveVisitor(visitor);}return uuid;}/*** 设置VisitLogger对象属性** @param joinPoint* @param visitLogger* @param result* @param times* @return*/private VisitLog handleLog(ProceedingJoinPoint joinPoint, VisitLogger visitLogger, HttpServletRequest request, Result result,int times, String identification) {String uri = request.getRequestURI();String method = request.getMethod(); //获取请求方法String ip = IpAddressUtils.getIpAddress(request);String userAgent = request.getHeader("User-Agent");//通过切面的ProceedingJoinPoint获取相关信息的工具类,在这里主要是获得请求参数Map<String, Object> requestParams = AopUtils.getRequestParams(joinPoint);//这里visitLogRemark是visitlog表中的一小部分VisitLogRemark visitLogRemark = judgeBehavior(visitLogger.value(), requestParams, result);VisitLog log = new VisitLog(identification, uri, method, visitLogger.value().getBehavior(),visitLogRemark.getContent(), visitLogRemark.getRemark(), ip, times, userAgent);log.setParam(StringUtils.substring(JacksonUtils.writeValueAsString(requestParams), 0, 2000));
// 将visitLog相关信息查询到后放到对象中并返回return log;}/*** 根据访问行为,设置对应的访问内容或备注** @param behavior* @param requestParams* @param result* @return*/private VisitLogRemark judgeBehavior(VisitBehavior behavior, Map<String, Object> requestParams, Result result) {String remark = "";String content = behavior.getContent();switch (behavior) {case INDEX:case MOMENT:remark = "第" + requestParams.get("pageNum") + "页";break;case BLOG:if (result.getCode() == 200) {BlogDetail blog = (BlogDetail) result.getData();String title = blog.getTitle();content = title;remark = "文章标题:" + title;}break;case SEARCH:if (result.getCode() == 200) {String query = (String) requestParams.get("query");content = query;remark = "搜索内容:" + query;}break;case CATEGORY:String categoryName = (String) requestParams.get("categoryName");content = categoryName;remark = "分类名称:" + categoryName + ",第" + requestParams.get("pageNum") + "页";break;case TAG:String tagName = (String) requestParams.get("tagName");content = tagName;remark = "标签名称:" + tagName + ",第" + requestParams.get("pageNum") + "页";break;case CLICK_FRIEND:String nickname = (String) requestParams.get("nickname");content = nickname;remark = "友链名称:" + nickname;break;}return new VisitLogRemark(content, remark);}
}
工具类:
@Slf4j
@Component
public class IpAddressUtils {/*** 在Nginx等代理之后获取用户真实IP地址** @param request* @return*/public static String getIpAddress(HttpServletRequest request) {String ip = request.getHeader("X-Real-IP");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("x-forwarded-for");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {//根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();} catch (UnknownHostException e) {log.error("getIpAddress exception:", e);}ip = inet.getHostAddress();}}return StringUtils.substringBefore(ip, ",");}private static DbSearcher searcher;private static Method method;/*** 在服务启动时加载 ip2region.db 到内存中* 解决打包jar后找不到 ip2region.db 的问题** @throws Exception 出现异常应该直接抛出终止程序启动,避免后续invoke时出现更多错误*/@PostConstructprivate void initIp2regionResource() throws Exception {InputStream inputStream = new ClassPathResource("/ipdb/ip2region.db").getInputStream();//将 ip2region.db 转为 ByteArraybyte[] dbBinStr = FileCopyUtils.copyToByteArray(inputStream);DbConfig dbConfig = new DbConfig();searcher = new DbSearcher(dbConfig, dbBinStr);//二进制方式初始化 DBSearcher,需要使用基于内存的查找算法 memorySearchmethod = searcher.getClass().getMethod("memorySearch", String.class);}/*** 根据ip从 ip2region.db 中获取地理位置** @param ip* @return*/public static String getCityInfo(String ip) {if (ip == null || !Util.isIpAddress(ip)) {log.error("Error: Invalid ip address");return "";}try {DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);String ipInfo = dataBlock.getRegion();if (!StringUtils.isEmpty(ipInfo)) {ipInfo = ipInfo.replace("|0", "");ipInfo = ipInfo.replace("0|", "");return ipInfo;}} catch (Exception e) {log.error("getCityInfo exception:", e);}return "";}
}
@Component
public class UserAgentUtils {private UserAgentAnalyzer uaa;public UserAgentUtils() {this.uaa = UserAgentAnalyzer.newBuilder().hideMatcherLoadStats().withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION_MAJOR).withField(UserAgent.AGENT_NAME_VERSION).build();}/*** 从User-Agent解析客户端操作系统和浏览器版本** @param userAgent* @return*/public Map<String, String> parseOsAndBrowser(String userAgent) {UserAgent agent = uaa.parse(userAgent);String os = agent.getValue(UserAgent.OPERATING_SYSTEM_NAME_VERSION_MAJOR);String browser = agent.getValue(UserAgent.AGENT_NAME_VERSION);Map<String, String> map = new HashMap<>();map.put("os", os);map.put("browser", browser);return map;}
}
public class AopUtils {private static Set<String> ignoreParams = new HashSet<String>() {{add("jwt");}};/*** 获取请求参数** @param joinPoint* @return*/public static Map<String, Object> getRequestParams(JoinPoint joinPoint) {Map<String, Object> map = new LinkedHashMap<>();String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); //获得切入点方法的参数的名字Object[] args = joinPoint.getArgs(); //获得参数对应的值for (int i = 0; i < args.length; i++) {if (!isIgnoreParams(parameterNames[i]) && !isFilterObject(args[i])) {map.put(parameterNames[i], args[i]);}}return map;}/*** consider if the data is file, httpRequest or response** @param o the data* @return if match return true, else return false*/private static boolean isFilterObject(final Object o) {return o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof MultipartFile;}/*** 判断是否忽略参数** @param params* @return*/private static boolean isIgnoreParams(String params) {return ignoreParams.contains(params);}
}
数据库
visitor
DROP TABLE IF EXISTS `visitor`;
CREATE TABLE `visitor` (`id` bigint(0) NOT NULL AUTO_INCREMENT,`uuid` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '访客标识码',`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip',`ip_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip来源',`os` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作系统',`browser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '浏览器',`create_time` datetime(0) NOT NULL COMMENT '首次访问时间',`last_time` datetime(0) NOT NULL COMMENT '最后访问时间',`pv` int(0) NULL DEFAULT NULL COMMENT '访问页数统计',`user_agent` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'user-agent用户代理',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `idx_uuid`(`uuid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
visit_log
DROP TABLE IF EXISTS `visit_log`;
CREATE TABLE `visit_log` (`id` bigint(0) NOT NULL AUTO_INCREMENT,`uuid` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访客标识码',`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求接口',`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方式',`param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求参数',`behavior` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访问行为',`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '访问内容',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',`ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip',`ip_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'ip来源',`os` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '操作系统',`browser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '浏览器',`times` int(0) NOT NULL COMMENT '请求耗时(毫秒)',`create_time` datetime(0) NOT NULL COMMENT '访问时间',`user_agent` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'user-agent用户代理',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
result
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Result {private Integer code;private String msg;private Object data;private Result(Integer code, String msg) {this.code = code;this.msg = msg;this.data = null;}private Result(Integer code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}public static Result ok(String msg, Object data) {return new Result(200, msg, data);}public static Result ok(String msg) {return new Result(200, msg);}public static Result error(String msg) {return new Result(500, msg);}public static Result error() {return new Result(500, "异常错误");}public static Result create(Integer code, String msg, Object data) {return new Result(code, msg, data);}public static Result create(Integer code, String msg) {return new Result(code, msg);}
}
public enum VisitBehavior {UNKNOWN("UNKNOWN", "UNKNOWN"),INDEX("访问页面", "首页"),ARCHIVE("访问页面", "归档"),MOMENT("访问页面", "动态"),FRIEND("访问页面", "友链"),ABOUT("访问页面", "关于我"),BLOG("查看博客", ""),CATEGORY("查看分类", ""),TAG("查看标签", ""),SEARCH("搜索博客", ""),CLICK_FRIEND("点击友链", ""),LIKE_MOMENT("点赞动态", ""),CHECK_PASSWORD("校验博客密码", ""),;/*** 访问行为*/private String behavior;/*** 访问内容*/private String content;VisitBehavior(String behavior, String content) {this.behavior = behavior;this.content = content;}public String getBehavior() {return behavior;}public String getContent() {return content;}
}
Controller
@VisitLogger(VisitBehavior.INDEX)@GetMapping("/blogs")public Result blogs(@RequestParam(defaultValue = "1") Integer pageNum) {PageResult<BlogInfo> pageResult = blogService.getBlogInfoListByIsPublished(pageNum);return Result.ok("请求成功", pageResult);}
关于service层的代码就不给出了,自己进行实现就行
基于博客系统的访客日志记录----代码合集相关推荐
- 基于博客系统的访客日志记录
当我们做的一些应用需要记录一些接口被访问时用户的信息时,我们就需要用到一些记录请求的技术,并记录日志到数据库.本文章使用的方法:注解+AOP 原理:事先在数据库中建立一个记录访客日志的一张表.先自定义 ...
- 基于SSM开发的校园访客登记系统 JAVA
10044基于SSM开发的校园访客登记系统 技术 Spring + SpringMVC + Mybatis 工具 eclipse + tomact + mysql + jdk 功能详情 前台功能 后台 ...
- 基于.NET 6 的开源访客管理系统
简单介绍一下系统功能 系统用于简化访客登记.查询.保存.传统的登记方式,不仅浪费纸张,而且还面临保存的问题,查阅不方便. 该系统为了在疫情期间能很好管理访客登记做好风险管控,同时可以整合智能设备做到自 ...
- 访客管理系统 - 天天访客
2020年初的一场突如其来的新冠疫情让全国各行各业变得措手不及.不论是疫情防控阶段,还是在复工复产阶段,人与人之间保持合理距离是切断病毒传波的根本方法.在后疫情时代,企业复工复产而产生的人员流动就对访 ...
- PHP之自定义阿里云客服在线访客名片
引言 在做电商模块开发的同学总会遇到这么一个需求,那就是售前售后的咨询,也就是所谓的客服功能,想要自己从零开发一个客服系统那也不是不可能,只是工程比较大,开发周期会就一些.如果想要快速上线的话,那就得 ...
- 自助访客登记与访客身份核验立式一体机
访客系统基本功能: 1.智能预约:可通过现场登记.网页提交信息.微信公众号预约等多种方式进行访客登记,可与企业OA系统对接,系统化管理,操作便捷. 2.证件+人脸身份验证:访客身份证.RFID访客卡. ...
- 抖音获客源码,短视频获客系统,获客SaaS有标准答案吗?
抖音获客源码,短视频获客系统,获客,这一SaaS行业的垂直细分领域,有没有可能,存在一种标准答案? 带着疑问打开浏览器,在某度中,我们尝试输入"抖音获客源码"."短视频获 ...
- 私人定制外贸精准获客产品 外贸智能获客系统 好选客
当下疫情奥密克戎全球施虐,严重影响外贸企业正常的全球采购商的开发,传统的外贸开发客户方式全球国际展会已经静止三年有余,目前疫情情况下还有很远的路要走.B2B国际网站模式过于传统导致同行竞品竞争激烈已毫 ...
- qt 历史记录控件_基于Qt图形界面软件的操作日志记录方法及系统_2015106293015_说明书_专利查询_专利网_钻瓜专利网...
技术领域 本发明涉及一种软件系统的日志记录技术,特别涉及一种基于Qt图形界面软件的操作日志记录方法及系统. 背景技术 软件操作日志是记录用户在使用软件的过程中,通过鼠标和键盘在操作界面上执行的点击和输 ...
最新文章
- ActiveMQ 学习(VM Transport)
- 海量数据处理之倒排索引
- c#中textbox属性_C#.Net中带有示例的TextBox.Multiline属性
- header()函数使用说明
- Android 中View仅仅能接收到ACTION_DOWN无法接收ACTION_MOVE和ACTION_UP解决的方法
- Docker部署Nebula Graph2.0和Studio
- Mysql 的ANY_VALUE()函数和 ONLY_FULL_GROUP_BY 模式
- Java中submit的方法,线程池中 submit()和 execute()方法区别
- [Python数据分析]NBA的球星们喜欢在哪个位置出手
- 无法删除IE图标(被劫持)
- 正是岳麓好风景,软件逢君正当时
- CSS优先级权重练习
- python和易语言哪个容易胖_碳水化合物和脂肪哪个更容易让身体发胖?
- 我的开源项目,趣享GIF源代码已正式公开
- elementplus 上传文件
- Rainbow 开发 step1
- Backtrader官方中文文档
- 深圳软件测试培训:移动测试ExpandableListView
- 家庭关系代码 GB/T 4761-2008
- 2022/5/12 选股记录
热门文章
- 华为麒麟9000性能提升幅度大,但恐难成安卓一哥
- UTC和东八区时间转换(python)
- CDH安装Tez 0.8.5
- 4大私域流量体系(个人号、公众号、社群和小程序)全方面价值对比:私域流量,企业保命之本爆发之源!...
- MMDetection3D 1.1:统一易用的 3D 感知平台
- 从坚果3的发布来看,锤子未来的发展将依然艰难
- 面对电车难题,自动驾驶会怎么选?
- 函函函函函函函函函函函数——two
- 字节跳动三场技术面+HR面,掌握这些知识点再也不怕面试通不过!
- 安卓虚拟摄像头_iPhone 的第四颗摄像头位置,为什么给了激光雷达?