如何在SpringBoot项目中,实现记录用户登录的IP地址及归属地信息?

news2025/7/6 10:21:31

在登录模块,我们经常要记录登录日志,其中比较重要的信息有ip地址和ip归属地,像我们公司开发的产品会提供给用户试用,因为我们做的是无人机应用方向的,即使试用也会产生费用,因为我们很多功能一旦用了就会消耗我们大量的资源,所以为了防止客户在试用时恶意传播账号,我们必须要记录用户的 登录ip 以及归属地,一旦遇到恶意传播的,轻则通知,重则警告,甚至不予试用,终止合作。

本文我将从我们的系统中划分出来一个简单的小功能:登录日志。

让我们直接开始!

一、说在前面

因为本身系统很庞大,加上代码的隐私性,我这边不会介绍非常多的属性,不过我能保证的是,代码你抄上去肯定能用。

二、数据库设计

按照开发流程,首先肯定是数据库的设计,我这边直接给出数据库建表语句:

CREATE TABLE `login_log` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) DEFAULT NULL COMMENT '登陆人姓名',
  `ip` varchar(255) DEFAULT NULL COMMENT '登录ip',
  `ip_attribution` varchar(255) DEFAULT NULL COMMENT 'ip归属地信息',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

在建表前,请保证你的数据库是创建好的,这个前提有点搞笑了。

由表可以看到,我们创建了login_log表,即登录日志表,字段名有id(主键)、name(登陆人姓名)、ip(登录ip)、ip_attribution(ip归属地信息)、create_time(创建时间)。

在真实的企业环境中,登录日志肯定远远不止这些,我是从我们的表中挑出了最基本的字段,像ip所属经纬度,因为需要调用第三方服务,我这边没有展示,当然市面上也有开源的根据ip获取经纬度的库,只不过不准确,想要准确还是要购买第三方服务。

三、代码编写

3.1 框架选型

我选用的框架是:

  • SpringBoot:2.7.0
  • jdk:1.8
  • mybatis-plus:3.4.2
  • lombok:1.18.22

还有其他基础的到时候报红的时候自行解决。

3.2 pom 依赖

跟ip归属地相关的依赖:

<dependency>
  <groupId>org.lionsoul</groupId>
  <artifactId>ip2region</artifactId>
  <version>1.7.2</version>
</dependency>

跟加载ip归属地数据库文件的依赖:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.5</version>
</dependency>

3.3 实体类

LoginLogEntity:

package com.wljlsmz.transitcenter.model.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.time.LocalDateTime;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: 用户登录日志实体类
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("login_log")
public class LoginLogEntity {

    /**
     * 主键
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 登陆人姓名
     */
    @TableField("name")
    private String name;

    /**
     * 登录ip
     */
    @TableField("ip")
    private String ip;

    /**
     * ip归属地信息
     */
    @TableField("ip_attribution")
    private String ipAttribution;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private LocalDateTime createTime;
}

3.4 Mapper类

LoginLogMapper:

package com.wljlsmz.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wljlsmz.transitcenter.model.entity.LoginLogEntity;

/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: 登录日志mapper接口类
 */
public interface LoginLogMapper extends BaseMapper<LoginLogEntity> {
}

3.5 service类

ILoginLogService:

package com.wljlsmz.transitcenter.service;

import com.wljlsmz.transitcenter.model.dto.LoginLogDTO;

/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: 登录日志服务接口类
 */
public interface ILoginLogService {

    int saveLoginLog(LoginLogDTO loginLogDTO);
}

LoginLogServiceImpl:

package com.wljlsmz.transitcenter.service.impl;

import com.wljlsmz.transitcenter.mapper.LoginLogMapper;
import com.wljlsmz.transitcenter.model.dto.LoginLogDTO;
import com.wljlsmz.transitcenter.model.entity.LoginLogEntity;
import com.wljlsmz.transitcenter.service.ILoginLogService;
import com.wljlsmz.transitcenter.util.CopyUtil;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: 登录日志服务接口实现类
 */
@Service
public class LoginLogServiceImpl implements ILoginLogService {

    @Autowired
    private LoginLogMapper mapper;

    @Override
    public int saveLoginLog(LoginLogDTO loginLogDTO) {
        LoginLogEntity loginLogEntity = CopyUtil.copy(loginLogDTO, LoginLogEntity.class);
        loginLogEntity.setCreateTime(LocalDateTime.now());
        return mapper.insert(loginLogEntity);
    }
}

3.6 IP工具类

IpUtils:

package com.wljlsmz.transitcenter.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.File;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;


/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: ip工具类
 */
@Slf4j
public class IpUtils {

    /**
     * 本地环回地址
     */
    private static final String LOCAL_IP = "127.0.0.1";

    /**
     * 未知
     */
    private static final String UNKNOWN = "unknown";

    public static String getIpAddr(HttpServletRequest request) {

        if (request == null) {
            return UNKNOWN;
        }

        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? LOCAL_IP : ip;
    }

    public static String getCityInfo(String ip) throws Exception {

        if (!Util.isIpAddress(ip)) {
            log.error("错误: 无效的ip地址");
            return null;
        }

        InputStream is = new PathMatchingResourcePatternResolver().getResources("ip2region.db")[0].getInputStream();
        File target = new File("ip2region.db");
        FileUtils.copyInputStreamToFile(is, target);
        is.close();

        if (StringUtils.isEmpty(String.valueOf(target))) {
            log.error("错误: 无效的ip2region.db文件");
            return null;
        }

        DbSearcher searcher = new DbSearcher(new DbConfig(), String.valueOf(target));

        try {
            DataBlock dataBlock = (DataBlock) searcher.getClass().getMethod("btreeSearch", String.class).invoke(searcher, ip);

            String ipInfo = dataBlock.getRegion();
            if (!StringUtils.isEmpty(ipInfo)) {
                ipInfo = ipInfo.replace("|0", "");
                ipInfo = ipInfo.replace("0|", "");
            }

            return ipInfo;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

上诉代码中提到了ip2region.db文件,这个文件需要放到resources目录下:

ip2region.db文件我放在网盘中,大家可以按需下载:

链接:https://pan.quark.cn/s/a5e187b7a91b
提取码:pjqQ

点击下载即可:

3.7 Controller类

这个类就使用到了以上所有的准备代码:

UserController:

package com.wljlsmz.transitcenter.controller;

import com.wljlsmz.transitcenter.common.response.ResponseResult;
import com.wljlsmz.transitcenter.model.dto.LoginDTO;
import com.wljlsmz.transitcenter.model.dto.LoginLogDTO;
import com.wljlsmz.transitcenter.service.ILoginLogService;
import com.wljlsmz.transitcenter.service.IUserService;
import com.wljlsmz.transitcenter.util.IpUtils;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;

/**
 * @author: wljlsmz
 * @date: 2022/11/15 10:28
 * @description: 用户接口类
 */
@RestController
@Slf4j
@Api(tags = "用户接口")
@RequestMapping("${url.user.prefix}${url.user.version}")
public class UserController {

    @Autowired
    private IUserService userService;

    @Autowired
    private ILoginLogService loginLogService;

    @ApiOperation("登录")
    @PostMapping("login")
    public ResponseResult login(@ApiParam @RequestBody LoginDTO loginDTO, HttpServletRequest request) {

        String ip = IpUtils.getIpAddr(request);
        String cityInfo = null;
        try {
            cityInfo = IpUtils.getCityInfo(ip);
        } catch (Exception e) {
            log.error("获取ip归属地信息失败!");
        }

        loginLogService.saveLoginLog(
                LoginLogDTO.builder()
                        .ip(ip)
                        .ipAttribution(cityInfo)
                        .name(loginDTO.getUserName())
                        .build());

        return userService.login(loginDTO);
    }
}

以上代码被我删减了好多,其实大家在使用的时候,值需要把下面这段代码摘出来放到自己的测试代码中即可:

String ip = IpUtils.getIpAddr(request);
String cityInfo = null;
try {
    cityInfo = IpUtils.getCityInfo(ip);
} catch (Exception e) {
    log.error("获取ip归属地信息失败!");
}

loginLogService.saveLoginLog(
        LoginLogDTO.builder()
                .ip(ip)
                .ipAttribution(cityInfo)
                .name(loginDTO.getUserName())
                .build());

测试代码

我们用swagger测试一下接口:

测试成功后,我们看下数据库:

成功记录了ip信息,在真实的企业环境中,你也再增加一些字段。

至此代码全部演示完毕。

总结

本文主要介绍了如何在登录接口增加登录日志的记录,文中的代码大家可以参考,有任何问题可以在下方评论区与我讨论,最后感谢您的阅读,您的点赞和转发就是我不断创作的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/9296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GFS分布式文件系统及其部署

目录 一、GlusterFS 1 MFS 2 GlusterFS 二 、GlusterFs特点 1 扩展性和高性能 2 高可用性 3 全局统一命名空间 4 弹性卷管理 5 基于标准协议 三 GlusterFS 术语 1 Brick(存储块) 2 volume(逻辑卷) 3 FUSE 4 VFS 5 Glusterd (后台管理进程) 四 模块化堆枝式架构…

深度支持赛事宣发,DF平台助推第三届全国人工智能大赛顺利举办!

由深圳市人民政府和鹏城实验室主办、深圳市科技创新委员会与新一代人工智能产业技术创新战略联盟联合承办的人工智能领域顶尖赛事——第三届全国人工智能大赛已圆满落幕。DataFountain大数据竞赛平台&#xff08;简称DF平台&#xff09;作为本次大赛的宣传合作伙伴&#xff0c;…

EFK部署centos7.9(二)head插件部署

安装配置head监控插件 本人是在ES服务器安装head插件是Nodejs实现的&#xff0c;所以需要先安装Nodejs。 wget https://nodejs.org/dist/v14.17.6/node-v14.17.6-linux-x64.tar.xz 下载安装包 tar xf node-v14.17.6-linux-x64.tar.xz -C /usr/local/ 解压安装包 vim /e…

PostgreSQL实战之物理复制和逻辑复制(三)

目录 PostgreSQL实战之物理复制和逻辑复制&#xff08;三&#xff09; 3.1 单实例、异步流复制、同步流复制性能测试 3.1 读性能测试 3.2 写性能测试 PostgreSQL实战之物理复制和逻辑复制&#xff08;三&#xff09; 3.1 单实例、异步流复制、同步流复制性能测试 根据Post…

智慧交通解决方案-最新全套文件

智慧交通解决方案-最新全套文件一、建设背景二、思路架构1、先进性2、全智慧化3、可靠性4、保密性三、解决方案四、获取 - 智慧交通全套最新解决方案合集一、建设背景 智能交通系统是未来交通系统的发展方向&#xff0c;它是将先进的信息技术、数据通讯传输技术、电子传感技术…

kt-connect使用-k8s流量代理

1.下载kt安装包 地址: https://github.com/alibaba/kt-connect/releases 2.下载k8s集群的config文件 cd /root/.kube 下载服务器的config文件3.安装config文件 切换到用户目录C:\Users\yangx创建.kube文件夹mkdir .kube目录结构为C:\Users\yangx\.kube把下载config文件移动到…

XSS进阶三

目录实验目的预备知识实验环境实验步骤一实例七、和实例六好像木有区别实验步骤二实例八、有时候你要跳出你的思维实验步骤三实例九、将xss进行到底实验目的 1.深入理解xss工作原理。 2.怎么去绕过规则实现xss。 3.培养学生的独立思考能力。 预备知识 XSS基础、XSS进阶一。 …

字节跳动测试岗面试记:二面被按地上血虐,所幸Offer已到手...

在互联网做了几年之后&#xff0c;去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好&#xff0c;更重要的是&#xff0c;它是对你专业能力的背书&#xff0c;大厂工作背景多少会给你的简历增加几分竞争力。 但说实话&#xff0c;想进大厂还真没那么容易。最近面试字…

[力扣] 剑指 Offer 第二天 - 从尾到头打印链表

[力扣] 剑指 Offer 第二天 - 从尾到头打印链表题目来源题目描述题目分析解题思路递归反转数组&#xff08;切片&#xff09;代码实现递归执行结果复杂度分析反转切片执行结果复杂度分析总结耐心和持久胜过激烈和狂热。 题目来源 来源&#xff1a;力扣&#xff08;LeetCode&…

【微服务】如何利用Nacos Config实现服务配置?

微服务--Nacos Config1、前言2、Nacos Config2.1 简介2.2 常见的服务配置中心3、Nacos Config入门4、Nacos Config深入4.1 配置动态刷新4.2 配置共享4.2.1 同一个微服务不同环境之间共享配置4.2.2 不同微服务之间共享配置5、Nacos的几个概念6、总结1、前言 在前期的文章中&…

纯代谢冲上Nature不是幻想,蛋氨酸饮食可影响小鼠癌症预后,同样可影响人体代谢

百趣代谢组学文献分享一篇题名 "Dietary methionine influences therapy in mouse cancer models and alters human metabolism"&#xff0c;发表在Nature。文章是做的纯代谢研究&#xff08;饮食影响癌症和代谢&#xff09;。纯代谢冲上Nature&#xff0c;这篇文章是…

C++:重定义:符号重定义:变量重定义(三):解决变量重定义(声明extern外部变量)

在上一篇文章中&#xff0c;我们知道&#xff1a;可以通过声明 const/ static 变量来隔离变量在源文件的可见性&#xff0c;来解决变量重复定义的问题&#xff0c; C&#xff1a;重定义&#xff1a;符号重定义&#xff1a;变量重定义&#xff08;二&#xff09;&#xff1a;解…

链表题目 : 链表的中间结点 与 链表中倒数第k个结点(leetcode 和 牛客)

坚持看完&#xff0c;结尾有思维导图总结 快慢指针在链表中的运用链表的中间结点步骤例子分析奇数情况偶数情况程序链表中倒数第k个结点步骤解析程序链表的中间结点 leetcode 链接 步骤 设置两个指针来遍历链表&#xff0c;一个叫 fast 每次跳过两个元素&#xff0c;一个叫 …

浅谈网络损伤仪HoloWAN的使用场景

目录一、HoloWAN 应用于卫星通讯领域1.1 卫星通讯领域的背景和挑战1.2 使用HoloWAN实现卫星⽹络仿真二、HoloWAN 应用于网络游戏2.1 网络游戏的背景和挑战2.2 使用HoloWAN模拟真实的网络环境三、HoloWAN 应用于数据中心迁移3.1 数据迁移的背景和挑战3.2 使用HoloWAN进行在线性能…

客户文章|南方医科大学李克玄团队破解肠道宏病毒与心肌病关系

近日凌恩生物合作客户南方医科大学南方医院麻醉科刘克玄团队在医学期刊《JOURNAL OF MEDICAL VIROLOGY》上发表名为“Gut microbiota dysbiosis is associated with sepsis-induced cardiomyopathy in patients: A case‒control study”研究&#xff0c;该研究利用宏基因组测序…

Imazing2023免费版苹果手机iOS数据管理软件

Imazing是一款很棒的苹果iOS管理工具&#xff0c;并且可以轻松扩大您对移动数据的控制范围&#xff0c;实现以往从未出现的功能&#xff0c;那么为了让大家更好的使用这款软件&#xff0c;因此小编就给大家带来了imazing使用教程&#xff0c;有需要的用户就一起来看看吧。一款安…

Linux安装java的JDK步骤

1.获取安装包&#xff08;服务器不通外网的话&#xff0c;自行在本机下载然后上传&#xff09; wget https://packages.baidu.com/app/jdk-8/jdk-8u121-linux-x64.tar.gz 2.创建安装目录 mkdir -p /usr/java 3.将前面下载的安装包移动到新目录 mv jdk-8u121-linux-x64.tar.g…

使用北鲲云在AWS上运行基因分析HPC任务

背景 近三十年来&#xff0c;生命科学与计算科学飞速发展。生物信息学是一门生命科学与计算科学的前沿交叉学科。生物信息学产生和迅猛发展的主要推动力来自于新一代测序等高通量技术在生命科学领域越来越广泛的应用。 基因组学是这一趋势的一个主要例子&#xff0c;其中高通量…

直播回顾|关联网络如何反团伙欺诈——标准答案版

11月10日下午15:00顶象数据科学家翼龙带来主题为《关联网络技术在业务安全中的应用》的直播。 直播内容包括常见的团伙欺诈场景、关联网络在反团伙欺诈中的作用、关联网络的技术框架&#xff0c;并就关联图谱构建和复杂网络算法展开了具体的讨论&#xff0c;最后以案例的形式形…

ElasticSearch 文档数据导入导出及两个ElasticSearch互相导入操作

文章目录一、前期安装准备二、ElasticSearch文档数据导入和导出三、两个ElasticSearch互相导入操作四、ElasticSearch版本不同是否有问题一、前期安装准备 1.先安装Node.js工具 Node.js官网下载地址&#xff1a;下载地址 双击下载完后的安装包&#xff0c;一直点下一步&#…