From 505b17263312df3b6d085a4dc806fabb8511ab7d Mon Sep 17 00:00:00 2001
From: xie7654 <765462425@qq.com>
Date: Sat, 5 Jul 2025 11:06:21 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96=E7=9C=9F?=
=?UTF-8?q?=E5=AE=9Eip?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 38 +++++++++----------
backend/system/views/user.py | 9 ++++-
backend/utils/ip_utils.py | 73 ++++++++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 22 deletions(-)
create mode 100644 backend/utils/ip_utils.py
diff --git a/README.md b/README.md
index d2b8ac3..2cc758e 100644
--- a/README.md
+++ b/README.md
@@ -13,26 +13,24 @@
## 功能截图
-### 部门管理
-
-
-### 用户管理
-
-
-### 角色管理
-
-
-### 岗位管理
-
-
-### 菜单管理
-
-
-### 前端界面
-
-
-### 普通员工界面
-
+
+
+ 部门管理
 |
+ 用户管理
 |
+
+
+ 角色管理
 |
+ 岗位管理
 |
+
+
+ 菜单管理
 |
+ 前端界面
 |
+
+
+ 权限员工界面
 |
+ |
+
+
# 许可证
diff --git a/backend/system/views/user.py b/backend/system/views/user.py
index 2167b6b..2b0abde 100644
--- a/backend/system/views/user.py
+++ b/backend/system/views/user.py
@@ -9,6 +9,7 @@ from rest_framework.permissions import IsAuthenticated
from django_filters import rest_framework as filters
from system.models import User, Menu, LoginLog, Dept
+from utils.ip_utils import get_client_ip
from utils.serializers import CustomModelSerializer
from utils.custom_model_viewSet import CustomModelViewSet
@@ -54,8 +55,12 @@ class UserLogin(ObtainAuthToken):
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
+
+ # 获取真实IP地址
+ client_ip = get_client_ip(request)
+
# 更新登录IP和登录时间
- user.login_ip = request.META.get('REMOTE_ADDR')
+ user.login_ip = client_ip
user.last_login = timezone.now()
user.save(update_fields=['login_ip', 'last_login'])
user_data = UserSerializer(user).data
@@ -63,7 +68,7 @@ class UserLogin(ObtainAuthToken):
LoginLog.objects.create(
username=user.username,
result=LoginLog.LoginResult.SUCCESS,
- user_ip=request.META.get('REMOTE_ADDR', ''),
+ user_ip=client_ip,
user_agent=request.META.get('HTTP_USER_AGENT', '')
)
# 在序列化后的数据中加入 accessToken
diff --git a/backend/utils/ip_utils.py b/backend/utils/ip_utils.py
new file mode 100644
index 0000000..642b157
--- /dev/null
+++ b/backend/utils/ip_utils.py
@@ -0,0 +1,73 @@
+def get_client_ip(request):
+ """
+ 获取客户端真实IP地址
+ 考虑代理服务器的情况,按优先级获取IP
+ """
+ # 按优先级顺序检查各种IP头
+ ip_headers = [
+ 'HTTP_X_FORWARDED_FOR', # 最常用的代理IP头
+ 'HTTP_X_REAL_IP', # Nginx 代理
+ 'HTTP_X_CLIENT_IP', # 客户端IP
+ 'HTTP_X_FORWARDED', # 转发IP
+ 'HTTP_FORWARDED_FOR', # 标准转发IP
+ 'HTTP_FORWARDED', # 标准转发
+ 'HTTP_CLIENT_IP', # 客户端IP
+ 'REMOTE_ADDR', # 直接连接IP
+ ]
+
+ for header in ip_headers:
+ ip = request.META.get(header)
+ if ip:
+ # 处理多个IP的情况(如 X-Forwarded-For: client, proxy1, proxy2)
+ if ',' in ip:
+ # 取第一个IP(客户端真实IP)
+ ip = ip.split(',')[0].strip()
+
+ # 验证IP格式
+ if is_valid_ip(ip):
+ return ip
+
+ # 如果都没有找到,返回 REMOTE_ADDR
+ return request.META.get('REMOTE_ADDR', '')
+
+
+def is_valid_ip(ip):
+ """
+ 验证IP地址格式是否有效
+ """
+ if not ip:
+ return False
+
+ # 简单的IP格式验证
+ parts = ip.split('.')
+ if len(parts) != 4:
+ return False
+
+ try:
+ for part in parts:
+ if not 0 <= int(part) <= 255:
+ return False
+ return True
+ except (ValueError, TypeError):
+ return False
+
+
+def get_client_ip_simple(request):
+ """
+ 简化版获取客户端IP(推荐用于大多数情况)
+ """
+ # 优先获取 X-Forwarded-For
+ x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
+ if x_forwarded_for:
+ # 取第一个IP
+ ip = x_forwarded_for.split(',')[0].strip()
+ if ip:
+ return ip
+
+ # 获取 X-Real-IP
+ x_real_ip = request.META.get('HTTP_X_REAL_IP')
+ if x_real_ip:
+ return x_real_ip
+
+ # 最后使用 REMOTE_ADDR
+ return request.META.get('REMOTE_ADDR', '')
\ No newline at end of file