PHP 实现 Redis 限制单 IP 访问频率的功能

有时候我们需要限制一个api或页面访问的频率,例如单ip或单用户一小时之内只能访问20次。

类似于这样的需求可以用Redis来高效地实现。

新建文件 IPRestrict.php

<?php
/**
 * PHP实现redis限制单ip、单用户的访问次数功能
 * Created by www.guihet.com.
**/
class IPRestrict {
    /**
     * @var Redis
     */
    private static $redis;
    /**
     * @var string
     * 取客户端真实ip地址作为key
     */
    private static $realip;
 
    public function __construct($host = '127.0.0.1', $port = 6379, $auth = null) {
        self::$redis = new Redis();
        self::$redis->connect('127.0.0.1', 6379);
        $auth && self::$redis->auth($auth);
        self::$realip = self::getRealIP();
    }
 
    /**
     * 取得客户端IP地址
     * @return string
     */
    public static function getRealIP() {
        $realip = "";
        if (isset($_SERVER)) {
            foreach(['HTTP_X_FORWARED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $name) {
                if (isset($_SERVER[$name])) {
                    $realip = $_SERVER[$name];
                    break;
                }
            }
        } else {
            $realip = getenv('HTTP_X_FORWARDED_FOR') || getenv('HTTP_CLIENT_IP') || getenv('REMOTE_ADDR');
        }
        return $realip;
    }
 
    /**
     * 记录该ip的访问次数 也可改成用户id
     * @param int $limit  限制指定时间内的访问磁珠
     * @param int $time  限制时间为60秒
     * @return int
     * @throws HttpException
     */
    public function requestCount($limit = 20, $time = 60*60) {
        $key = self::$realip;
        $redis = self::$redis;
        /**
         * @var bool
         */
        $exists = $redis->exists($key);
        $redis->incr($key);
 
        if ($exists) {
            $count = $redis->get($key);
            if ($count > $limit) {
                throw new Exception('请求太频繁,请稍后再试!'.$count);
            }
        } else {
            // 首次计数 设定过期时间
            $redis->expire($key, $time);
        }
 
        return $redis->get($key);
    }
}
?>

新建测试文件 ip.php

<?php
include 'IPRestrict.php';

$restrict = new IPRestrict();
echo $restrict->requestCount();
?>

这样访问 ip.php 就输出访问次数。
 

为了更加准确的获取访问用户 ip,可以使用以下函数。

<?php
/**
 * 获取客户端IP地址
 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
 * @param boolean $adv 是否进行高级模式获取(有可能被伪装) 
 * @return mixed
 */
function get_client_ip($type = 0,$adv=false) {
    $type       =  $type ? 1 : 0;
    static $ip  =   NULL;
    if ($ip !== NULL) return $ip[$type];
    if($adv){
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr    =   explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos    =   array_search('unknown',$arr);
            if(false !== $pos) unset($arr[$pos]);
            $ip     =   trim($arr[0]);
        }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip     =   $_SERVER['HTTP_CLIENT_IP'];
        }elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip     =   $_SERVER['REMOTE_ADDR'];
        }
    }elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ip     =   $_SERVER['REMOTE_ADDR'];
    }
    // IP地址合法验证
    $long = sprintf("%u",ip2long($ip));
    $ip   = $long ? array($ip, $long) : array('0.0.0.0', 0);
    return $ip[$type];
}
?>

调用 get_client_ip(0, true); 即可

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注