本文目录导读:

我将为您提供一个PHP项目中的IP归属查询与风控系统的实现方案,这个系统包含IP地理位置查询、风险评分、访问频率限制等功能。
IP归属查询模块
1 IP地理位置查询类
<?php
namespace App\Utils;
class IpLocation
{
private $ipdbPath;
private $ipdb = null;
public function __construct($ipdbPath = null)
{
$this->ipdbPath = $ipdbPath ?? __DIR__ . '/../../data/ip2region.xdb';
}
/**
* 获取IP地理位置信息
*/
public function getLocation($ip)
{
// 支持多种数据源,按优先级获取
$location = $this->getFromIp2Region($ip);
if (!$location) {
$location = $this->getFromApi($ip);
}
return $location;
}
/**
* 使用ip2region离线数据库
*/
private function getFromIp2Region($ip)
{
try {
if (!file_exists($this->ipdbPath)) {
return null;
}
// 使用XDB搜索引擎
$searcher = new \Ip2Region();
$result = $searcher->btreeSearch($ip);
if ($result && isset($result['region'])) {
$regions = explode('|', $result['region']);
return [
'country' => $regions[0] !== '0' ? $regions[0] : '',
'region' => $regions[1] !== '0' ? $regions[1] : '',
'province' => $regions[2] !== '0' ? $regions[2] : '',
'city' => $regions[3] !== '0' ? $regions[3] : '',
'isp' => $regions[4] !== '0' ? $regions[4] : '',
'full_location' => $result['region']
];
}
} catch (\Exception $e) {
// 记录日志
error_log("IP查询失败: " . $e->getMessage());
}
return null;
}
/**
* 使用在线API查询
*/
private function getFromApi($ip)
{
// 防止内网IP查询
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$url = "http://ip-api.com/json/{$ip}?lang=zh-CN";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
if ($data && $data['status'] === 'success') {
return [
'country' => $data['country'] ?? '',
'region' => $data['regionName'] ?? '',
'province' => $data['regionName'] ?? '',
'city' => $data['city'] ?? '',
'isp' => $data['isp'] ?? '',
'lat' => $data['lat'] ?? 0,
'lon' => $data['lon'] ?? 0,
'timezone' => $data['timezone'] ?? '',
'full_location' => "{$data['country']} {$data['regionName']} {$data['city']}"
];
}
}
}
return null;
}
/**
* 获取客户端真实IP
*/
public function getClientIp()
{
$ip = '';
// 检查HTTP代理头
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
'HTTP_X_CLUSTER_CLIENT_IP'
];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ipList = explode(',', $_SERVER[$header]);
$ip = trim($ipList[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
break;
}
}
}
// 如果获取不到,使用REMOTE_ADDR
if (empty($ip) && isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
}
2 IP数据库更新脚本
<?php
namespace App\Console;
class UpdateIpDatabase
{
private $downloadUrl = 'https://github.com/lionsoul2014/ip2region/raw/master/data/ip2region.xdb';
private $localPath;
public function __construct($localPath = null)
{
$this->localPath = $localPath ?? __DIR__ . '/../../data/ip2region.xdb';
}
public function update()
{
$backupPath = $this->localPath . '.backup';
try {
// 备份旧数据库
if (file_exists($this->localPath)) {
copy($this->localPath, $backupPath);
}
// 下载新数据库
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->downloadUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_FOLLOWLOCATION => true
]);
$data = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && strlen($data) > 1000) {
file_put_contents($this->localPath, $data);
echo "IP数据库更新成功\n";
return true;
} else {
throw new \Exception("下载失败: HTTP {$httpCode}");
}
} catch (\Exception $e) {
// 恢复备份
if (file_exists($backupPath)) {
copy($backupPath, $this->localPath);
unlink($backupPath);
}
echo "更新失败: " . $e->getMessage() . "\n";
return false;
}
// 删除备份
if (file_exists($backupPath)) {
unlink($backupPath);
}
}
}
风控系统模块
1 风控引擎类
<?php
namespace App\RiskEngine;
class RiskEngine
{
private $redis;
private $rules = [];
private $score = 0;
private $riskDetails = [];
public function __construct($redis = null)
{
$this->redis = $redis ?? new \Redis();
$this->loadDefaultRules();
}
/**
* 加载默认规则
*/
private function loadDefaultRules()
{
$this->rules = [
'frequency' => [
'enabled' => true,
'limit' => 100, // 每分钟最大请求数
'weight' => 30, // 违规权重
'time_window' => 60 // 时间窗口(秒)
],
'geography' => [
'enabled' => true,
'black_countries' => ['KP', 'IR', 'SY'], // 高风险国家
'high_risk_countries' => ['RU', 'CN', 'NG'],
'weight' => 40
],
'proxy' => [
'enabled' => true,
'weight' => 25
],
'device_fingerprint' => [
'enabled' => true,
'max_devices_per_ip' => 5,
'weight' => 20
],
'behavior' => [
'enabled' => true,
'speed_limit' => 5, // 每秒操作数
'weight' => 35
]
];
}
/**
* 执行风控检查
*/
public function evaluate($context)
{
$this->score = 0;
$this->riskDetails = [];
// 执行各规则检查
$results = [
$this->checkFrequency($context),
$this->checkGeography($context),
$this->checkProxy($context),
$this->checkDeviceFingerprint($context),
$this->checkBehavior($context)
];
// 汇总结果
foreach ($results as $result) {
if ($result['risk']) {
$this->score += $result['points'];
$this->riskDetails[] = $result['detail'];
}
}
return [
'score' => $this->score,
'level' => $this->getRiskLevel(),
'details' => $this->riskDetails,
'action' => $this->getAction()
];
}
/**
* 检查访问频率
*/
private function checkFrequency($context)
{
$rule = $this->rules['frequency'];
if (!$rule['enabled']) {
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
$ip = $context['ip'];
$key = "risk:frequency:{$ip}";
$current = $this->redis->incr($key);
if ($current === 1) {
$this->redis->expire($key, $rule['time_window']);
}
if ($current > $rule['limit']) {
return [
'risk' => true,
'points' => $rule['weight'],
'detail' => "访问频率过高: {$current}/{$rule['limit']}"
];
}
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
/**
* 检查地理位置风险
*/
private function checkGeography($context)
{
$rule = $this->rules['geography'];
if (!$rule['enabled'] || empty($context['country_code'])) {
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
$countryCode = strtoupper($context['country_code']);
if (in_array($countryCode, $rule['black_countries'])) {
return [
'risk' => true,
'points' => 100,
'detail' => "高风险国家: {$countryCode}"
];
}
if (in_array($countryCode, $rule['high_risk_countries'])) {
return [
'risk' => true,
'points' => $rule['weight'],
'detail' => "中风险国家: {$countryCode}"
];
}
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
/**
* 检查代理/VPN
*/
private function checkProxy($context)
{
$rule = $this->rules['proxy'];
if (!$rule['enabled']) {
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
$isProxy = false;
$proxyType = '';
// 检查常见的代理标识
if (!empty($context['headers'])) {
$headers = $context['headers'];
// 检查常见的代理头部
$proxyHeaders = [
'HTTP_VIA',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED',
'HTTP_X_FORWARDED_HOST',
'HTTP_X_PROXY_ID',
'HTTP_PROXY_CONNECTION'
];
foreach ($proxyHeaders as $header) {
if (!empty($headers[$header])) {
$isProxy = true;
$proxyType = $header;
break;
}
}
}
// 检查代理IP数据库
if (!$isProxy && !empty($context['ip'])) {
$proxyIpKey = "proxy_ips:{$context['ip']}";
$isProxy = $this->redis->sIsMember('known_proxy_ips', $context['ip']);
}
if ($isProxy) {
return [
'risk' => true,
'points' => $rule['weight'],
'detail' => "检测到代理/VPN: {$proxyType}"
];
}
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
/**
* 检查设备指纹
*/
private function checkDeviceFingerprint($context)
{
$rule = $this->rules['device_fingerprint'];
if (!$rule['enabled'] || empty($context['device_id'])) {
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
$ip = $context['ip'];
$deviceId = $context['device_id'];
$key = "risk:devices:{$ip}";
// 记录设备关联
$this->redis->sAdd($key, $deviceId);
$this->redis->expire($key, 86400); // 24小时
$deviceCount = $this->redis->sCard($key);
if ($deviceCount > $rule['max_devices_per_ip']) {
return [
'risk' => true,
'points' => $rule['weight'],
'detail' => "设备数量异常: {$deviceCount}/{$rule['max_devices_per_ip']}"
];
}
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
/**
* 检查行为模式
*/
private function checkBehavior($context)
{
$rule = $this->rules['behavior'];
if (!$rule['enabled']) {
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
$userId = $context['user_id'] ?? 'anonymous';
$action = $context['action'] ?? 'unknown';
$key = "risk:behavior:{$userId}:{$action}";
// 记录操作时间戳
$now = microtime(true);
$this->redis->zAdd($key, $now, $now . ':' . uniqid());
$this->redis->expire($key, 10); // 10秒窗口
// 清除过期数据
$this->redis->zRemRangeByScore($key, 0, $now - 10);
// 计算操作速度
$count = $this->redis->zCard($key);
if ($count > $rule['speed_limit']) {
return [
'risk' => true,
'points' => $rule['weight'],
'detail' => "操作速度异常: {$count}/{$rule['speed_limit']}"
];
}
return ['risk' => false, 'points' => 0, 'detail' => ''];
}
/**
* 获取风险等级
*/
private function getRiskLevel()
{
if ($this->score >= 100) return 'critical';
if ($this->score >= 70) return 'high';
if ($this->score >= 40) return 'medium';
if ($this->score >= 20) return 'low';
return 'safe';
}
/**
* 获取建议操作
*/
private function getAction()
{
switch ($this->getRiskLevel()) {
case 'critical':
return 'block';
case 'high':
return 'challenge_captcha';
case 'medium':
return 'require_verification';
case 'low':
return 'monitor';
default:
return 'allow';
}
}
/**
* 封禁IP
*/
public function blockIp($ip, $minutes = 60)
{
$key = "blocked_ips:{$ip}";
$this->redis->setEx($key, $minutes * 60, '1');
}
/**
* 检查IP是否被封禁
*/
public function isIpBlocked($ip)
{
return $this->redis->exists("blocked_ips:{$ip}");
}
/**
* 获取风控详情
*/
public function getRiskDetails()
{
return $this->riskDetails;
}
}
2 风控中间件
<?php
namespace App\Http\Middleware;
use App\RiskEngine\RiskEngine;
use App\Utils\IpLocation;
use Closure;
class RiskControlMiddleware
{
private $riskEngine;
private $ipLocation;
public function __construct(RiskEngine $riskEngine, IpLocation $ipLocation)
{
$this->riskEngine = $riskEngine;
$this->ipLocation = $ipLocation;
}
public function handle($request, Closure $next)
{
$ip = $this->ipLocation->getClientIp();
// 检查IP是否被封禁
if ($this->riskEngine->isIpBlocked($ip)) {
return response()->json([
'code' => 403,
'message' => '访问被限制'
], 403);
}
// 获取地理位置信息
$location = $this->ipLocation->getLocation($ip);
// 构建风控上下文
$context = [
'ip' => $ip,
'user_id' => auth()->id(),
'device_id' => $request->header('X-Device-ID'),
'user_agent' => $request->userAgent(),
'headers' => $request->headers->all(),
'action' => $request->route()->getName() ?? 'unknown',
'country_code' => $location['country_code'] ?? '',
'request_params' => $request->all()
];
// 执行风控检查
$result = $this->riskEngine->evaluate($context);
// 根据风控结果执行操作
switch ($result['action']) {
case 'block':
$this->riskEngine->blockIp($ip, 30);
return response()->json([
'code' => 403,
'message' => '访问被限制',
'risk_score' => $result['score']
], 403);
case 'challenge_captcha':
return response()->json([
'code' => 401,
'message' => '需要验证',
'captcha_required' => true,
'risk_score' => $result['score']
], 401);
case 'require_verification':
// 记录风险日志
$this->logRiskEvent($ip, $result);
break;
case 'monitor':
// 仅记录日志
$this->logRiskEvent($ip, $result, 'info');
break;
}
return $next($request);
}
private function logRiskEvent($ip, $result, $level = 'warning')
{
// 记录风控日志到数据库或日志文件
\Log::channel('risk')->log($level, "Risk event detected", [
'ip' => $ip,
'score' => $result['score'],
'level' => $result['level'],
'details' => $result['details'],
'timestamp' => now()
]);
}
}
3 风控记录模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class RiskRecord extends Model
{
protected $table = 'risk_records';
protected $fillable = [
'ip',
'user_id',
'risk_score',
'risk_level',
'action_taken',
'details',
'user_agent',
'request_url',
'request_method'
];
protected $casts = [
'details' => 'array',
'created_at' => 'datetime'
];
/**
* 获取某个IP的风险统计
*/
public static function getIpRiskStats($ip, $days = 7)
{
return self::where('ip', $ip)
->where('created_at', '>=', now()->subDays($days))
->selectRaw('
COUNT(*) as total_events,
AVG(risk_score) as avg_score,
MAX(risk_score) as max_score,
COUNT(CASE WHEN risk_level = "critical" THEN 1 END) as critical_count,
COUNT(CASE WHEN risk_level = "high" THEN 1 END) as high_count
')
->first();
}
/**
* 获取高风险IP列表
*/
public static function getHighRiskIps($threshold = 70, $limit = 100)
{
return self::where('risk_score', '>=', $threshold)
->where('created_at', '>=', now()->subHours(24))
->groupBy('ip')
->selectRaw('ip, COUNT(*) as event_count, AVG(risk_score) as avg_score')
->orderByDesc('avg_score')
->limit($limit)
->get();
}
}
4 风控统计服务
<?php
namespace App\Services;
use App\Models\RiskRecord;
use Illuminate\Support\Facades\Redis;
class RiskStatisticsService
{
/**
* 获取实时风控仪表盘数据
*/
public function getDashboardData()
{
return [
'total_requests_today' => $this->getTodayTotalRequests(),
'blocked_ips_count' => $this->getBlockedIpsCount(),
'high_risk_events' => $this->getHighRiskEventsCount(),
'current_risk_level' => $this->getCurrentRiskLevel(),
'top_risk_ips' => $this->getTopRiskIps(),
'risk_trend' => $this->getRiskTrend()
];
}
/**
* 获取今天的总请求数
*/
private function getTodayTotalRequests()
{
$key = 'stats:requests:' . date('Y-m-d');
return Redis::get($key) ?? 0;
}
/**
* 获取被封禁的IP数量
*/
private function getBlockedIpsCount()
{
// 使用Redis记录被封禁IP
$keys = Redis::keys('blocked_ips:*');
return count($keys);
}
/**
* 获取高风险事件数量
*/
private function getHighRiskEventsCount()
{
return RiskRecord::where('risk_level', 'high')
->where('created_at', '>=', now()->subHours(24))
->count();
}
/**
* 获取当前风险等级
*/
private function getCurrentRiskLevel()
{
$highRiskCount = $this->getHighRiskEventsCount();
if ($highRiskCount > 1000) return 'critical';
if ($highRiskCount > 500) return 'high';
if ($highRiskCount > 100) return 'medium';
return 'low';
}
/**
* 获取风险最高的IP
*/
private function getTopRiskIps($limit = 10)
{
return RiskRecord::selectRaw('ip, COUNT(*) as count, AVG(risk_score) as avg_score')
->where('created_at', '>=', now()->subHours(24))
->groupBy('ip')
->orderByDesc('avg_score')
->limit($limit)
->get();
}
/**
* 获取风险趋势
*/
private function getRiskTrend()
{
$trend = [];
for ($i = 6; $i >= 0; $i--) {
$date = now()->subHours($i * 4);
$count = RiskRecord::where('created_at', '>=', $date)
->where('created_at', '<', $date->copy()->addHours(4))
->count();
$trend[] = [
'time' => $date->format('H:i'),
'count' => $count
];
}
return $trend;
}
}
数据库迁移文件
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRiskTables extends Migration
{
public function up()
{
// 风险记录表
Schema::create('risk_records', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('ip', 45)->index();
$table->unsignedBigInteger('user_id')->nullable()->index();
$table->integer('risk_score')->default(0);
$table->string('risk_level', 20)->default('safe');
$table->string('action_taken', 50)->nullable();
$table->json('details')->nullable();
$table->string('user_agent', 500)->nullable();
$table->string('request_url', 500)->nullable();
$table->string('request_method', 10)->nullable();
$table->timestamps();
$table->index(['ip', 'created_at']);
$table->index(['risk_level', 'created_at']);
});
// IP黑名单表
Schema::create('ip_blacklist', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('ip', 45)->unique();
$table->string('reason', 500)->nullable();
$table->timestamp('blocked_until')->nullable();
$table->unsignedBigInteger('blocked_by')->nullable();
$table->timestamps();
$table->index('blocked_until');
});
// IP白名单表
Schema::create('ip_whitelist', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('ip', 45)->unique();
$table->string('description', 500)->nullable();
$table->timestamps();
});
// 风险规则配置表
Schema::create('risk_rules', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 100)->unique();
$table->string('description', 500)->nullable();
$table->json('config')->nullable();
$table->boolean('enabled')->default(true);
$table->integer('priority')->default(0);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('risk_records');
Schema::dropIfExists('ip_blacklist');
Schema::dropIfExists('ip_whitelist');
Schema::dropIfExists('risk_rules');
}
}
使用示例
1 在控制器中使用
<?php
namespace App\Http\Controllers;
use App\RiskEngine\RiskEngine;
use App\Utils\IpLocation;
use Illuminate\Http\Request;
class ExampleController extends Controller
{
private $riskEngine;
private $ipLocation;
public function __construct(RiskEngine $riskEngine, IpLocation $ipLocation)
{
$this->riskEngine = $riskEngine;
$this->ipLocation = $ipLocation;
}
public function login(Request $request)
{
$ip = $this->ipLocation->getClientIp();
$location = $this->ipLocation->getLocation($ip);
// 创建风控上下文
$context = [
'ip' => $ip,
'user_id' => $request->input('user_id'),
'device_id' => $request->header('X-Device-ID'),
'user_agent' => $request->userAgent(),
'action' => 'login',
'country_code' => $location['country_code'] ?? '',
];
// 执行风控检查
$result = $this->riskEngine->evaluate($context);
if ($result['action'] === 'block') {
$this->riskEngine->blockIp($ip, 60);
return response()->json([
'message' => '登录失败,请稍后重试',
'risk_score' => $result['score']
], 403);
}
if ($result['action'] === 'challenge_captcha') {
return response()->json([
'message' => '需要验证码验证',
'captcha_required' => true
], 401);
}
// 正常登录流程
// ...
}
}
2 配置文件
<?php
// config/risk.php
return [
'enabled' => env('RISK_CONTROL_ENABLED', true),
'redis' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'ip_database' => [
'path' => storage_path('app/ip2region.xdb'),
'auto_update' => false,
'update_interval' => 7, // 天
],
'rules' => [
'frequency' => [
'enabled' => true,
'limit' => 100,
'time_window' => 60,
'weight' => 30,
],
'geography' => [
'enabled' => true,
'black_countries' => ['KP', 'IR', 'SY'],
'high_risk_countries' => ['RU', 'CN', 'NG'],
'weight' => 40,
],
'proxy' => [
'enabled' => true,
'weight' => 25,
],
'device_fingerprint' => [
'enabled' => true,
'max_devices_per_ip' => 5,
'weight' => 20,
],
'behavior' => [
'enabled' => true,
'speed_limit' => 5,
'weight' => 35,
],
],
'actions' => [
'block' => [
'duration' => 30, // 分钟
'response_code' => 403,
],
'challenge_captcha' => [
'response_code' => 401,
],
],
'logging' => [
'channel' => 'risk',
'level' => 'warning',
],
];
这个实现包含:
- IP归属查询:支持离线数据库和在线API
- 风控引擎:多维度风险评估
- 访问频率控制:基于Redis的计数器
- 地理位置风控:国家/地区黑白名单
- 代理检测:识别代理/VPN流量
- 设备指纹:设备关联分析
- 行为分析:操作速度检测
- 完整的中间件:自动执行风控检查
- 统计和监控:风控数据可视化支持
系统会根据风险评分自动采取相应措施,如封禁、验证码验证、监控等。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播