PHP 怎么动态脱敏

wen PHP项目 1

本文目录导读:

PHP 怎么动态脱敏

  1. 基础函数封装法
  2. 配置驱动的脱敏
  3. 使用魔术方法动态处理
  4. 支持注解的脱敏(需要反射)
  5. 使用第三方库(推荐:PHP-Mask)
  6. 动态规则配置示例
  7. 使用建议

PHP 实现动态脱敏(数据掩码)有多种方法,以下是几种常用的实现方案:

基础函数封装法

<?php
class DataMasker {
    /**
     * 手机号脱敏:保留前3后4
     */
    public static function maskMobile($mobile) {
        if (empty($mobile)) return '';
        return substr($mobile, 0, 3) . '****' . substr($mobile, -4);
    }
    /**
     * 身份证号脱敏:保留前6后4
     */
    public static function maskIdCard($idCard) {
        if (empty($idCard)) return '';
        $len = strlen($idCard);
        if ($len < 10) return $idCard;
        return substr($idCard, 0, 6) . str_repeat('*', $len - 10) . substr($idCard, -4);
    }
    /**
     * 银行卡号脱敏:保留前4后4
     */
    public static function maskBankCard($card) {
        if (empty($card)) return '';
        return substr($card, 0, 4) . '**** **** **** ' . substr($card, -4);
    }
    /**
     * 邮箱脱敏
     */
    public static function maskEmail($email) {
        if (empty($email)) return '';
        list($username, $domain) = explode('@', $email);
        $maskedUsername = substr($username, 0, 1) . str_repeat('*', strlen($username) - 1);
        return $maskedUsername . '@' . $domain;
    }
    /**
     * 姓名脱敏:保留姓
     */
    public static function maskName($name) {
        if (empty($name)) return '';
        if (mb_strlen($name) == 1) return $name;
        return mb_substr($name, 0, 1) . str_repeat('*', mb_strlen($name) - 1);
    }
    /**
     * 通用脱敏方法
     */
    public static function mask($data, $type) {
        switch ($type) {
            case 'mobile':   return self::maskMobile($data);
            case 'idcard':   return self::maskIdCard($data);
            case 'bankcard': return self::maskBankCard($data);
            case 'email':    return self::maskEmail($data);
            case 'name':     return self::maskName($data);
            default:         return $data;
        }
    }
}

配置驱动的脱敏

<?php
class DynamicMasker {
    // 脱敏规则配置
    private static $rules = [
        'user' => [
            'mobile'    => ['function' => 'maskMobile'],
            'id_card'   => ['function' => 'maskIdCard'],
            'email'     => ['function' => 'maskEmail'],
            'real_name' => ['function' => 'maskName'],
            'address'   => ['function' => 'maskCustom', 'params' => ['start' => 3, 'end' => 2]],
        ],
        'order' => [
            'order_no'   => ['function' => 'maskCustom', 'params' => ['start' => 2, 'end' => 2]],
            'customer_name' => ['function' => 'maskName'],
        ]
    ];
    /**
     * 根据场景和字段动态脱敏
     */
    public static function maskData(array $data, $scene) {
        if (!isset(self::$rules[$scene])) {
            return $data;
        }
        $result = $data;
        foreach (self::$rules[$scene] as $field => $rule) {
            if (isset($result[$field])) {
                $result[$field] = self::{$rule['function']}(
                    $result[$field], 
                    $rule['params'] ?? []
                );
            }
        }
        return $result;
    }
    // 自定义脱敏:保留开头和结尾指定长度
    private static function maskCustom($value, $params = []) {
        $start = $params['start'] ?? 0;
        $end = $params['end'] ?? 0;
        $len = mb_strlen($value);
        if ($len <= $start + $end) {
            return str_repeat('*', $len);
        }
        return mb_substr($value, 0, $start) . 
               str_repeat('*', $len - $start - $end) . 
               mb_substr($value, -$end);
    }
    // 其他方法同第一种方案...
}

使用魔术方法动态处理

<?php
class MagicMasker {
    private $rawData;
    private $maskFields = [
        'mobile'   => 'maskMobile',
        'idcard'   => 'maskIdCard',
        'bankcard' => 'maskBankCard',
    ];
    public function __construct(array $data) {
        $this->rawData = $data;
    }
    public function __get($name) {
        if (isset($this->rawData[$name])) {
            // 如果是敏感字段,直接脱敏
            if (isset($this->maskFields[$name])) {
                $method = $this->maskFields[$name];
                return DataMasker::$method($this->rawData[$name]);
            }
            return $this->rawData[$name];
        }
        return null;
    }
    // 获取脱敏后的完整数据
    public function toArray() {
        $result = [];
        foreach ($this->rawData as $key => $value) {
            if (isset($this->maskFields[$key])) {
                $method = $this->maskFields[$key];
                $result[$key] = DataMasker::$method($value);
            } else {
                $result[$key] = $value;
            }
        }
        return $result;
    }
}
// 使用示例
$userData = [
    'name' => '张三',
    'mobile' => '13812345678',
    'idcard' => '110101199001011234',
    'email' => 'zhangsan@example.com'
];
$maskedUser = new MagicMasker($userData);
echo $maskedUser->mobile;  // 138****5678

支持注解的脱敏(需要反射)

<?php
class AnnotatedMasker {
    /**
     * @Mask(type="mobile")
     */
    public $mobile;
    /**
     * @Mask(type="idcard")
     */
    public $idCard;
    public function process($className) {
        $reflection = new ReflectionClass($className);
        $properties = $reflection->getProperties();
        foreach ($properties as $property) {
            $docComment = $property->getDocComment();
            if (preg_match('/@Mask\(type="(\w+)"\)/', $docComment, $matches)) {
                $type = $matches[1];
                unserialize($property->getName());
            }
        }
    }
}

使用第三方库(推荐:PHP-Mask)

composer require makinacorpus/php-mask
<?php
use MakinaCorpus\PHP-Mask\Masker;
$masker = new Masker();
$masked = $masker->mask($data, 'mobile'); // 直接脱敏

动态规则配置示例

<?php
class DynamicConfigMasker {
    // 从数据库或配置中心加载规则
    private function getRules($scene) {
        // 模拟从数据库加载配置
        return [
            'mobile' => [
                'type' => 'replace',
                'start' => 3,
                'end' => 4,
                'char' => '*'
            ],
            'email' => [
                'type' => 'regex',
                'pattern' => '/^(.*?)(@.*)$/',
                'replacement' => '$1***$2'
            ]
        ];
    }
    public function mask(array $data, $scene) {
        $rules = $this->getRules($scene);
        foreach ($rules as $field => $rule) {
            if (!isset($data[$field])) continue;
            switch ($rule['type']) {
                case 'replace':
                    $data[$field] = $this->maskByRange(
                        $data[$field], 
                        $rule['start'], 
                        $rule['end'], 
                        $rule['char'] ?? '*'
                    );
                    break;
                case 'regex':
                    $data[$field] = preg_replace(
                        $rule['pattern'], 
                        $rule['replacement'], 
                        $data[$field]
                    );
                    break;
            }
        }
        return $data;
    }
}

使用建议

  1. 根据业务场景选择:如果是简单需求,使用函数封装即可
  2. 动态配置:如果规则经常变化,建议使用配置驱动
  3. 性能考虑:大量数据脱敏时,使用数组映射比对象更高效
  4. 安全性:脱敏操作应该在数据输出前统一处理,避免遗漏

这些方法可以根据你的具体需求灵活选择或组合使用。

抱歉,评论功能暂时关闭!