前后端实现解析用户请求ip地址

前言

在我的软件系统中,如果希望安全系数高一些的话,往往会有用户登陆行为表来记录用户登陆行为,保障用户账号安全,比如记录登陆地址,每次登陆时候读取数据最近几次登陆地点,进行账号安全验证
假如以下是我的用户登陆行为表

在这里插入图片描述

实现获取用户登陆地址的方法有很多种,比如通过前端整合第三方gps库发送地址,也可以通过整合ip地址来查询,我这里采取第二种,既然已经获取到了ip,那么就直接使用ip进行解析

前端操作

首先在前后端项目中,前端文件时是部署给nginx来完成负载均衡的,这里我们看nginx的配置文件

nginx主配置

nginx.conf

#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}
##nginx.conf   把里面注释的内容和静态资源配置相关删除,文件加载 http模块中有许多的server 一个server表示一个虚拟主机
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
	# 引入自定义配置文件
	include web.conf/*.conf;
}

这里我是将我前端的配置文件全部放在web.conf目录下的,所以主配置文件只需要负载导入

前端项目的具体

gate-way.conf (我的配置名)

upstream admin-gateway{
    server localhost:51603;
}

server {
	listen 8803;
	location / {
		root html/admin-web/;
		index index.html;
	}

	location ~/service_6001/(.*) {
		proxy_pass http://admin-gateway/$1;
		proxy_set_header HOST $host;  # 不改变源请求头的值
		proxy_pass_request_body on;  #开启获取请求体
		proxy_pass_request_headers on;  #开启获取请求头
		proxy_set_header X-Real-IP $remote_addr;   # 记录真实发出请求的客户端IP 用于记录ip
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #记录代理信息
	}
}

解析:

  • localtion模块配置前端读取的根目录
  • listen该前端的端口
  • upstream admin-gateway反向代理的具体地址
  • proxy_set_header X-Real-IP $remote_addr; 重点是这个
  • ~/service_6001/(.*) 是一个正则表达式,用于匹配以 “/service_6001/” 开头的请求路径,并捕获括号内的内容。
  • proxy_pass http://admin-gateway/$1;:

proxy_pass 用于指定被代理的服务器地址。
在这里,http://admin-gateway 是代理目标的基础地址。
$1 是正则表达式中捕获的第一个分组的变量,即括号内的内容。这是用于在代理目标中构建新的路径
比如
/service_6001/login 代理到admin-gateway就是localhost:51603/login

比如在这里插入图片描述
此时真实请求地址是 localhost:51603/admin/login/in

后端操作

这里使用ip2region , Ip2region开源项目,github地址:https://github.com/lionsoul2014/ip2region。一个离线地址库可以根据ip地址来查询文件中的地址,准确率高达99.9%,吹的话就不说了,这里可以看github查看所有优点
这里直接将整合

导入依赖
  <!-- ip2region -->
        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>2.6.3</version>
        </dependency>
       <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

ip2region 用于 IP 地址解析,提供了高性能的 IP 地址定位功能。
commons-io 提供了一些常见的、与 I/O 操作相关的工具类,用于简化和增强 Java 的 I/O 操作。

下载离线数据集

去官网下载整个项目,需要的是解压后的data/xdb文件
在这里插入图片描述
在这里插入图片描述
然后新建ip2region目录把数据文件导入
目录结构
在这里插入图片描述

工具类
import org.apache.commons.io.FileUtils;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.File;
import java.text.MessageFormat;
import java.util.Objects;

public class AddressUtil {

    /**
     * 当前记录地址的本地DB
     */
    private static final String TEMP_FILE_DIR = "/home/admin/app/";

    /**
     * 根据IP地址查询登录来源
     *
     * @param ip
     * @return
     */
    public static String getCityInfo(String ip) {
        try {
            // 获取当前记录地址位置的文件
            String dbPath = Objects.requireNonNull(AddressUtil.class.getResource("/ip2region/ip2region.xdb")).getPath();
            File file = new File(dbPath);
            //如果当前文件不存在,则从缓存中复制一份
            if (!file.exists()) {
                dbPath =    TEMP_FILE_DIR + "ip.db";
                System.out.println(MessageFormat.format("当前目录为:[{0}]", dbPath));
                file = new File(dbPath);
                FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.xdb")), file);
            }
            //创建查询对象
            Searcher searcher = Searcher.newWithFileOnly(dbPath);
            //开始查询
            return searcher.searchByStr(ip);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //默认返回空字符串
        return "";
    }
//todo 测试
    public static void main(String[] args) {
        System.out.println(getCityInfo(""));
    }
}

测试这里ip是我的服务器
在这里插入图片描述

库有了,需要ip地址才能解析地方

获取请求ip

之前配置前端是携带真实请求地址的,那么只需要解析即可
这里我直接在controller进行解析
controller

@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
AdUserLoginService userService;
    @PostMapping("/in")

    public ResponseResult login(@RequestBody AdUserDto dto, HttpServletRequest request){
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        System.out.println("登陆用户的ip地址为"+ ipAddress);

    return userService.login(dto,ipAddress);

    }


}

获取 x-forwarded-for 头部信息:
x-forwarded-for 是一个常用的 HTTP 头部字段,用于表示客户端的原始 IP 地址,如果请求经过了代理服务器,这个字段可能包含了多个 IP 地址,通过逗号分隔。所以在这里,首先尝试从这个字段中获取用户的 IP 地址。

  1. 如果 x-forwarded-for 为空或者为 “unknown”,尝试获取其他头部信息:

if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    ipAddress = request.getHeader("WL-Proxy-Client-IP");
}

如果 x-forwarded-for 为空或者为 “unknown”,则尝试从其他可能包含用户 IP 地址的头部信息中获取。这包括 Proxy-Client-IP 和 WL-Proxy-Client-IP 头部。
3.
如果上述头部信息都未获取到有效的 IP 地址,使用 request.getRemoteAddr():

if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    ipAddress = request.getRemoteAddr();
}

如果前面的头部信息都未获取到有效的 IP 地址,最后使用 request.getRemoteAddr() 获取客户端的 IP 地址。

这里的controller路径是login开头而不是admin/开头是因为我这里使用了网关转发时候省略了前缀

spring:
  cloud:
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true 
        # 将全局CORS配置添加到SimpleUrlHandlerMapping
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*" 
            # 允许所有请求头
            allowedOrigins: "*" 
            # 允许所有来源
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
               # 允许的HTTP请求方法,包括OPTIONS(通常应该是OPTIONS而不是OPTION)
      routes:
        - id: admin
          uri: lb://admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix= 1

#去掉请求路径中的一个前缀(通常是去掉服务名的前缀) filters 下的 - StripPrefix=1: 当请求匹配到这个路由规则时,会执行此处定义的过滤器。StripPrefix=1 表示去掉请求路径中的一个前缀。在这个场景下,如果请求路径是 /admin/example,经过这个过滤器后,会将 /admin 去掉,转发到后端服务的路径就变成了 /example。

好了这个不是重点,继续讲业务

测试controller是否能获取ip,测试成功在这里插入图片描述
具体实现业务的

业务代码

Service

@Service
public class AdUserLoginServiceImpl extends ServiceImpl<AdUserLoginMapper, AdUserLogin>
    implements AdUserLoginService {
@Autowired
AdUserMapper adUserMapper;
    @Override
    public ResponseResult login(AdUserDto dto, String ipAddress) {
        //1.检查参数
        if(StringUtils.isBlank(dto.getName()) || StringUtils.isBlank(dto.getPassword())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"用户名或密码为空");
        }
        //2.查询用户
        AdUser user = adUserMapper.selectOne(Wrappers.<AdUser>lambdaQuery().eq(AdUser::getName, dto.getName()));
        if(user == null){
//            说明没有该用户
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //2.1.检查密码
        //3.比对密码
        String salt = user.getSalt();
        String pwd = dto.getPassword();
//        获取盐加密和数据库密码比较
        String codeepwd = DigestUtils.md5DigestAsHex((pwd + salt).getBytes());
        if(codeepwd.equals(user.getPassword())){
            //4.封装返回数据  jwt
            Map<String,Object> map  = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(user.getId().longValue()));
            user.setSalt("");
            user.setPassword("");
            map.put("user",user);
//            写入登陆行为
            AdUserLogin adUserLogin = new AdUserLogin();
            adUserLogin.setUserId(user.getId());
            // 字符在字符串中的位置索引,如果字符不存在,则返回 -1。通过判断索引是否为 -1
            if (ipAddress.indexOf(',')!=-1){
//                一样的效果
//                ipAddress=ipAddress.split(",")[0];
                 ipAddress = ipAddress.substring(0,ipAddress.indexOf(','));
            }
            adUserLogin.setIp(ipAddress);
//            保存用户登陆地址
            adUserLogin.setAddress(AddressUtil.getCityInfo(ipAddress));
            adUserLogin.setCreatedTime(new Date());
            save(adUserLogin);
            return ResponseResult.okResult(map);

        }else {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

听GPT 讲Rust源代码--src/tools(18)

File: rust/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs 在Rust源代码中的from_comment.rs文件位于Rust分析器&#xff08;rust-analyzer&#xff09;工具的ide-ssr库中&#xff0c;它的作用是将注释转换为Rust代码。 具体来说&#xff0c;该文件实现了从注…

机器学习 | 概率图模型

见微知著&#xff0c;睹始知终。 见到细微的苗头就能预知事物的发展方向&#xff0c;能透过微小的现象看到事物的本质&#xff0c;推断结论或者结果。 概率模型为机器学习打开了一扇新的大门&#xff0c;将学习的任务转变为计算变量的概率分布。 实际情况中&#xff0c;各个变量…

java easyexcel上传和下载数据

安装依赖 easyexcel官方文档 <!--通过注解的方式导出excel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.1</version></dependency>注意踩坑&#xff1a;easyexcel会…

Linux服务器 部署飞书信息发送服务

项目介绍&#xff1a; 飞书信息发送服务是指将飞书信息发送服务部署到一个Linux服务器上。飞书是一款企业级的即时通讯和协作工具&#xff0c;支持发送消息给飞书的功能。通过部署飞书信息发送服务&#xff0c;可以方便内网发送信息给外网飞书。 项目代码结构展示&#xff1a; …

51单片机(STC8)-- 串口配置及串口重定向(printf)

文章目录 STC8串口概述串口寄存器配置串口1控制寄存器SCON串口1数据寄存器SBUF串口1模式 1工作方式串口1波特率计算方式 串口注意事项串口1通信demo串口重定向 STC8串口概述 由下图可知STC8H3K64S4带有4个4个串行通信接口&#xff0c;芯片名后两位S所带的数字即代表这款芯片带…

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成

一、插件 Settings... 1.1 File -- Settings 1.2 Settings -- Plugins 1.2.1 搜索框&#xff0c;也可以直接搜索 1.3 Plugins -- 【输入 & 搜索】mybatis 1.3.1 插件不同功能介绍 1.3.2 翻译如下 1.4 选中 Update&#xff0c;更新下 1.4.1 更新中 1.4.2 Restart IDE 1…

华为全屋wifi6蜂鸟套装标准

华为政企42 华为政企 目录 上一篇华为安防监控摄像头下一篇华为企业级无线路由器

STM32CubeMX驱动ST7789

环境 1、单片机:STM32F103C8T6 2、开发平台&#xff1a;STM32CUBEMXkeil mdk 3、屏幕&#xff1a;ST7789&#xff0c;分辨率240*240 STM32配置 1、使用硬件SPI1驱动屏幕。配置如下&#xff1a; 2、屏幕控制引脚配置&#xff1a; 注意&#xff1a;只配置了DC,RST,CS这3个控…

重塑数字生产力体系,生成式AI将开启云计算未来新十年?

科技云报道原创。 今天我们正身处一个历史的洪流&#xff0c;一个巨变的十字路口。生成式AI让人工智能技术完全破圈&#xff0c;带来了机器学习被大规模采用的历史转折点。 它掀起的新一轮科技革命&#xff0c;远超出我们今天的想象&#xff0c;这意味着一个巨大的历史机遇正…

人工智能对网络安全的影响

技术的快速发展带来了不断增长的威胁环境&#xff0c;网络犯罪分子和恶意行为者利用我们互联世界中的漏洞。在这个数字时代&#xff0c;数据泄露和网络攻击呈上升趋势&#xff0c;仅靠传统的安全措施已经不够了。人工智能 &#xff08;AI&#xff09; 的进步彻底改变了网络安全…

【Midjourney】Midjourney根据prompt提示词生成黑白色图片

目录 &#x1f347;&#x1f347;Midjourney是什么&#xff1f; &#x1f349;&#x1f349;Midjourney怎么用&#xff1f; &#x1f514;&#x1f514;提示词格式 &#x1f34b;&#x1f34b;应用示例——“秘密花园”式涂色书配图生成 &#x1f34c;&#x1f34c;例子1…

推荐算法架构7:特征工程

1 概述 特征工程[1]&#xff08;Feature Engineering&#xff09;是推荐算法的基础&#xff0c;它对收集到的原始数据进行解析和变换&#xff0c;从而提取出模型所需要的信息。通过挖掘丰富和高质量的特征&#xff0c;并对其进行合理的处理&#xff0c;可以提升模型预估准确度…

【SVN】centos7搭建svn--亲测能通

centos7.6搭建svn 1 知识小课堂1.1 CentOS1.2 SVN 2 搭建过程2.1 前期准备2.2 通过yum命令安装svnserve2.3 创建版本库目录2.4 创建svn版本库2.5 配置修改2.5 防火墙配置2.6 启动或关闭svn服务器2.6.1 进程守护2.6.2 检测svn端口3690是否已经监听&#xff1a;2.6.3 关闭SVN 2.7…

【2023CANN训练营第二季】——Ascend C算子开发(进阶)微认证

1.微认证题目&#xff1a; 参考tensorflow的Sinh算子&#xff0c;实现Ascend C算子Sinh,算子命名为SinhCustom&#xff0c;并完成aclnn算子调用相关算法: sinh(x) (exp(x) - exp(-x)) / 2.0 要求: 1、完成host侧和kernel侧代码实现。 2、实现sinh功能&#xff0c;支持float16…

图论 | 网络流的基本概念

文章目录 流网路残留网络增广路径割最大流最小割定理最大流Edmonds-Karp 算法算法步骤程序代码时间复杂度 流网路 流网络&#xff1a; G ( V , E ) G (V, E) G(V,E) 有向图&#xff0c;不考虑反向边s&#xff1a;源点t&#xff1a;汇点 c ( u , v ) c(u, v) c(u,v)&#xff…

使用 Taro 开发鸿蒙原生应用 —— 探秘适配鸿蒙 ArkTS 的工作原理

背景 在上一篇文章中&#xff0c;我们已经了解到华为即将发布的鸿蒙操作系统纯血版本——鸿蒙 Next&#xff0c;以及各个互联网厂商开展鸿蒙应用开发的消息。其中&#xff0c;Taro作为一个重要的前端开发框架&#xff0c;也积极适配鸿蒙的新一代语言框架 —— ArkTS。 本文将…

网络安全-API接口安全

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 API接口概念 API接口&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&…

Flink 状态管理与容错机制(CheckPoint SavePoint)的关系

一、什么是状态 无状态计算的例子&#xff1a; 例如一个加法算子&#xff0c;第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是&#xff0c;相同的输入都会得到相同的结果&#xff0c;与次数无关。 有状态计算的例子&#xff1a; 访问量的统计&#x…

【科学计算语言】实验三 Python复杂数据类型

【目的和要求】 &#xff08;1&#xff09;掌握Python语言中的组合数据类型 &#xff08;2&#xff09;掌握列表、元组、字典、集合及字符串的基本应用 &#xff08;3&#xff09;熟练运用有关序列操作的Python内置函数 【实验准备】 【实验内容】 1. 实验练习&#xff1a;掌握…

HTML5的完整学习笔记

HTML 什么是HTML&#xff1a; 作为前端三件套之一&#xff0c;HTML的全称是超文本标记语言&#xff08;Hypertext Markup Language&#xff09;。HTML是一种标记语言&#xff0c;用于创建网页。它由一系列标签组成&#xff0c;这些标签用于定义网页的结构和内容。HTML标签告诉…