Skip to content

Commit 16b25c7

Browse files
authored
add offline ip location resolution (#78)
1 parent 926a3f9 commit 16b25c7

File tree

8 files changed

+43
-6
lines changed

8 files changed

+43
-6
lines changed

backend/app/common/exception/exception_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def all_exception_handler(request: Request, exc: Exception):
108108

109109
else:
110110
import traceback
111+
111112
log.exception(traceback.format_exc())
112113
return JSONResponse(
113114
status_code=500,

backend/app/common/log.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616

1717
class Logger:
18-
1918
def __init__(self):
2019
self.log_path = path_conf.LogPath
2120

backend/app/core/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
from functools import lru_cache
4+
from typing import Literal
45

56
from pydantic import BaseSettings, root_validator
67

@@ -55,7 +56,7 @@ def validator_api_url(cls, values):
5556
STATIC_FILES: bool = False
5657

5758
# Location Parse
58-
LOCATION_PARSE: bool = True # 将会导致登录延时,建议关闭,有条件自行使用第三方离线数据库
59+
LOCATION_PARSE: Literal['online', 'offline', 'false'] = 'offline'
5960

6061
# MySQL
6162
DB_ECHO: bool = False

backend/app/core/path_conf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717

1818
# RBAC model.conf 文件路径
1919
RBAC_MODEL_CONF = os.path.join(BasePath, 'app', 'core', settings.CASBIN_RBAC_MODEL_NAME)
20+
21+
# 离线 IP 数据库路径
22+
IP2REGION_XDB = os.path.join(BasePath, 'app', 'static', 'ip2region.xdb')

backend/app/services/login_log_service.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ async def create(
3030
ip = await request_parse.get_request_ip(request)
3131
user_agent = request.headers.get('User-Agent')
3232
_, os_info, browser = str(parse(user_agent)).replace(' ', '').split('/')
33-
location = await request_parse.get_location(ip, user_agent) if settings.LOCATION_PARSE else '未知'
33+
if settings.LOCATION_PARSE == 'online':
34+
location = await request_parse.get_location_online(ip, user_agent)
35+
elif settings.LOCATION_PARSE == 'offline':
36+
location = request_parse.get_location_offline(ip)
37+
else:
38+
location = '未知'
3439
obj_in = CreateLoginLog(
3540
user_uuid=user.user_uuid,
3641
username=user.username,

backend/app/static/ip2region.xdb

10.6 MB
Binary file not shown.

backend/app/utils/request_parse.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
import httpx
4+
from XdbSearchIP.xdbSearcher import XdbSearcher
45
from httpx import HTTPError
56
from fastapi import Request
67

8+
from backend.app.core.path_conf import IP2REGION_XDB
9+
710

811
async def get_request_ip(request: Request) -> str:
9-
"""获取请求的ip地址"""
12+
"""获取请求的 ip 地址"""
1013
real = request.headers.get('X-Real-IP')
1114
if real:
1215
ip = real
@@ -19,8 +22,14 @@ async def get_request_ip(request: Request) -> str:
1922
return ip
2023

2124

22-
async def get_location(ipaddr: str, user_agent: str) -> str:
23-
"""获取ip地址归属地(临时)"""
25+
async def get_location_online(ipaddr: str, user_agent: str) -> str:
26+
"""
27+
在线获取 ip 地址属地,无法保证可用性,准确率较高
28+
29+
:param ipaddr:
30+
:param user_agent:
31+
:return:
32+
"""
2433
async with httpx.AsyncClient(timeout=3) as client:
2534
ip_api_url = f'http://ip-api.com/json/{ipaddr}?lang=zh-CN'
2635
whois_url = f'http://whois.pconline.com.cn/ipJson.jsp?ip={ipaddr}&json=true'
@@ -35,3 +44,21 @@ async def get_location(ipaddr: str, user_agent: str) -> str:
3544
except (HTTPError, KeyError):
3645
city = None
3746
return city or '未知' if city != '' else '未知'
47+
48+
49+
def get_location_offline(ipaddr: str) -> str:
50+
"""
51+
离线获取 ip 地址属地,无法保证准确率,100%可用
52+
53+
:param ipaddr:
54+
:return:
55+
"""
56+
cb = XdbSearcher.loadContentFromFile(dbfile=IP2REGION_XDB)
57+
searcher = XdbSearcher(contentBuff=cb)
58+
data = searcher.search(ipaddr)
59+
searcher.close()
60+
location_info = data.split('|')
61+
country = location_info[0]
62+
province = location_info[2]
63+
city = location_info[3]
64+
return city if city != '0' else province if province != '0' else country if country != '0' else '未知'

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ tzlocal==2.1
3535
user_agents==2.2.0
3636
uvicorn[standard]==0.13.4
3737
wait-for-it==2.2.1
38+
XdbSearchIP==1.0.2

0 commit comments

Comments
 (0)