本文目录导读:

为PHP项目配置黑白名单可以基于IP地址、用户ID、请求来源、API密钥等不同维度,下面介绍几种常见且可落地的配置方法,按复杂度从低到高排列:
基于IP地址的黑白名单(最常用)
简单实现(硬编码或配置文件)
config/access.php
<?php
return [
// IP白名单
'whitelist' => [
'192.168.1.100',
'10.0.0.0/24', // 支持CIDR
'172.16.0.0-172.16.0.255' // 支持范围
],
// IP黑名单
'blacklist' => [
'203.0.113.0/24',
'198.51.100.50'
],
'mode' => 'whitelist' // 或 'blacklist'
];
中间件/入口检查
src/Middleware/AccessControl.php
<?php
namespace App\Middleware;
class AccessControl
{
private $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function handle($request, $next)
{
$ip = $request->getClientIp();
// 检查黑名单(黑名单优先于白名单)
if ($this->isBlacklisted($ip)) {
http_response_code(403);
exit('Access denied');
}
// 如果是白名单模式,检查是否在白名单中
if ($this->config['mode'] === 'whitelist') {
if (!$this->isWhitelisted($ip)) {
http_response_code(403);
exit('Access denied');
}
}
return $next($request);
}
private function isWhitelisted($ip)
{
return $this->matchIpList($ip, $this->config['whitelist']);
}
private function isBlacklisted($ip)
{
return $this->matchIpList($ip, $this->config['blacklist']);
}
private function matchIpList($ip, $list)
{
foreach ($list as $rule) {
if ($this->ipMatches($ip, $rule)) {
return true;
}
}
return false;
}
private function ipMatches($ip, $rule)
{
if (strpos($rule, '/')) {
// CIDR 匹配
list($subnet, $bits) = explode('/', $rule);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
return ($ip & $mask) === ($subnet & $mask);
} elseif (strpos($rule, '-')) {
// 范围匹配
list($start, $end) = explode('-', $rule);
$ip = ip2long($ip);
return $ip >= ip2long($start) && $ip <= ip2long($end);
} else {
// 精确匹配
return $ip === $rule;
}
}
}
使用示例(入口文件)
public/index.php
<?php
$accessConfig = require '../config/access.php';
$accessControl = new \App\Middleware\AccessControl($accessConfig);
// 假设使用某个框架的Request对象
$accessControl->handle($request, function($req) {
// 正常业务逻辑
echo "Welcome!";
});
基于数据库的动态黑白名单
适合需要后台管理、频繁更新规则的场景。
数据库结构
CREATE TABLE access_list (
id INT AUTO_INCREMENT PRIMARY KEY,
type ENUM('whitelist', 'blacklist') NOT NULL,
value VARCHAR(255) NOT NULL, -- IP、用户ID、域名等
match_type ENUM('ip', 'user_id', 'domain', 'path') DEFAULT 'ip',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
PHP 查询与匹配
<?php
class DynamicAccessControl
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function check($type, $value)
{
// 先检查黑名单
$stmt = $this->db->prepare(
"SELECT COUNT(*) FROM access_list
WHERE type = 'blacklist' AND value = ? AND match_type = ?"
);
$stmt->execute([$value, $type]);
if ($stmt->fetchColumn() > 0) {
return false;
}
// 如果是白名单模式,再检查白名单
$stmt = $this->db->prepare(
"SELECT COUNT(*) FROM access_list
WHERE type = 'whitelist' AND value = ? AND match_type = ?"
);
$stmt->execute([$value, $type]);
return $stmt->fetchColumn() > 0;
}
}
基于用户认证的黑白名单
适用于需要按用户级别控制访问的场景。
<?php
class UserAccessControl
{
const ROLE_WHITELIST = [
'admin' => ['*'], // 管理员全部允许
'editor' => ['/admin/*', '/api/*'], // 编辑可访问特定路径
'user' => ['/profile', '/content/*'] // 普通用户
];
const USER_BLACKLIST = [
1001, 1005, 2003 // 被封禁的用户ID
];
public function check($userId, $userRole, $requestPath)
{
// 黑名单用户
if (in_array($userId, self::USER_BLACKLIST)) {
http_response_code(403);
exit('Your account has been suspended');
}
// 白名单路径检查
if (!isset(self::ROLE_WHITELIST[$userRole])) {
return false;
}
$allowedPaths = self::ROLE_WHITELIST[$userRole];
foreach ($allowedPaths as $pattern) {
if (fnmatch($pattern, $requestPath)) {
return true;
}
}
return false;
}
}
基于Nginx/Apache的服务器级别控制(性能最优)
Nginx 配置
# IP白名单
location /admin {
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
}
# 全局黑名单
location / {
deny 203.0.113.0/24;
deny 198.51.100.50;
# 其他正常配置
}
Apache .htaccess
# 白名单
<RequireAny>
Require ip 192.168.1.0/24
Require ip 10.0.0.1
</RequireAny>
# 黑名单
<RequireAll>
Require all granted
Require not ip 203.0.113.0/24
Require not ip 198.51.100.50
</RequireAll>
综合最佳实践
<?php
class AdvancedAccessControl
{
private $cache = [];
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function checkRequest($request)
{
// 1. 快速缓存检查(Redis/Memcached)
$cacheKey = 'acl:' . $request->getClientIp();
if (isset($this->cache[$cacheKey])) {
return $this->cache[$cacheKey];
}
// 2. 多维度检查
$checkResult = $this->multiDimensionalCheck($request);
// 3. 缓存结果(5分钟)
$this->cache[$cacheKey] = $checkResult;
// 4. 也可以写入Redis
// Redis::setex($cacheKey, 300, (int)$checkResult);
return $checkResult;
}
private function multiDimensionalCheck($request)
{
$ip = $request->getClientIp();
$userId = $request->getUserId();
$path = $request->getPath();
$userAgent = $request->getUserAgent();
$referer = $request->getReferer();
// 黑名单优先级最高
if ($this->isInBlacklist($ip, $userId, $path)) {
return false;
}
// 白名单模式检查
if (ACCESS_MODE === 'whitelist') {
return $this->isInWhitelist($ip, $userId, $path);
}
// 混合模式:API路径同时检查IP和UserID
if (strpos($path, '/api/') === 0) {
return $this->checkApiAccess($ip, $userId);
}
return true; // 默认允许
}
private function checkApiAccess($ip, $userId)
{
// 内部API只能由特定IP访问
if (strpos($path, '/api/internal/') === 0) {
return in_array($ip, INTERNAL_IPS);
}
// 公开API需要token中携带的白名单状态
return true;
}
}
关键注意事项
| 方面 | 建议 |
|---|---|
| 性能 | 生产环境优先用Nginx/Apache层控制,PHP层做次要校验 |
| 缓存 | 每次请求都查数据库会严重降低性能,建议用Redis缓存规则 |
| 日志 | 所有被拒绝的请求应当记录日志(IP、时间、路径、原因) |
| 监控 | 黑名单命中率异常升高可能是攻击,建议设置告警 |
| 更新 | 动态列表变动后要有机制清除缓存(如Redis Pub/Sub) |
简单场景:用方法1(配置文件+中间件)
需要管理后台:用方法2(数据库动态列表)
高并发:用方法4(Nginx层)+ PHP层二次校验
复杂权限:综合方法3和方法5
选择哪种方式取决于你的项目规模、并发量、以及是否需要动态管理规则。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播