Write the Code. Change the World.

12月 16

做聊天项目的时候,通常会用到关键词过滤。对于一些敏感信息,可能还会进行内容加密。这里从两个不同的功能点,总结下过程。也是在别人的基础上,跑了一遍而已。照着做,可以一步到位。

环境
1. php
2. js
3. laravel

敏感词过滤

https://github.com/FireLustre/php-dfa-sensitive

composer require lustre/php-dfa-sensitive

# 项目中
use DfaFilter\SensitiveHelper;

# 添加词库,使用文件
$path = storage_path('sensitive/words.txt');
$handle = SensitiveHelper::init()->setTreeByFile($wordFilePath);

# 添加词库,单个添加
$data = [
    '哎呦我的天',
    '太阳',
    '太阳块融化我的脸'
];

$handle = SensitiveHelper::init()->setTree($data);

# 替换,检测
$islegal = $handle->islegal($content);

$content = '啥啥啥,哎呦我的天呀,太阳快融化我的脸';

$content = $handle->replace($content, '*', true);

上边这些,github 上都有,照着做就可以了。

https://www.zkii.net/tech/php/1115.html

数据加密解密

这里数据加密使用 aes。数据加密解密的对象是字符串。无论哪 js 还是 php,都可以互相转换。不过这里可能存在一种文体,js 加密的 js 可以解密,但 php 不能解密。反之亦然。出现这种情况,往往是加密解密模式没有配对上。

** js 加密解密 **

参考: https://blog.csdn.net/yingbaoyu/article/details/95761177

如果不是 node 环境,可以下载 https://pan.baidu.com/s/1Mvg8vhlD56wvS4b6yjUoaA 提取码:57gd

例子:


const aseKey = 'YjKp7COQ9QZN2EgMJdiI8tzBsJarvQAr'; const aseIv = 'zmtuC5UyMfK3r1QYXKa1lYmNNy8F5jiP'; //将秘钥转换成Utf8字节数组 const key = CryptoJS.enc.Utf8.parse(aseKey); const iv = CryptoJS.enc.Utf8.parse(aseIv); let data = { name: 'vini', gender: 1 } data = JSON.stringify(data); let encoded = CryptoJS.AES.encrypt(data, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString(); console.log(encoded); let uncoded = CryptoJS.AES.decrypt(encoded, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString(CryptoJS.enc.Utf8); console.log(uncoded); # 这个时候, data 应该等于 uncoded

key 的长度决定了 CBC 模式的不同。

# php 加密解密

<?php

namespace App\Helper;

class Aes
{
    static public function encrypt($data)
    {
        $method = 'AES-256-CBC';
        $key = 'YjKp7COQ9QZN2EgMJdiI8tzBsJarvQAr';
        $iv = 'zmtuC5UyMfK3r1QY';
        return openssl_encrypt($data, $method, $key, 0, $iv);
    }

    static public function decrypt($data)
    {
        $method = 'AES-256-CBC';
        $key = 'YjKp7COQ9QZN2EgMJdiI8tzBsJarvQAr';
        $iv = 'zmtuC5UyMfK3r1QY';
        return openssl_decrypt($data, $method, $key, 0, $iv);
    }
}

这里为了方便,把 method key iv 都写死了。项目里边不会这样做。比如,可以定义一个类:

<?php
namespace App\Utils;

class Aes
{
    protected $aesSecret = 'YjKp7COQ9QZN2EgMJdiI8tzBsJarvQAr', $aesIv = '00000000000000000000000000000000';

    /**
     * 进制转换
     * @param string $hex
     * @return string
     */
    protected function aesHexIv($hex = '')
    {
        $string = '';
        $hex = $hex != '' ? $hex : $this->aesIv;
        for ($i = 0; $i < strlen($hex) - 1; $i += 2) {
            $string .= chr(hexdec($hex[$i] . $hex[$i + 1]));
        }
        return $string;
    }

    /**
     * AES加密
     * @param string $content
     * @param string $key
     * @param string $hex
     * @return string
     */
    public function aesEncrypt($content = '', $key = '', $hex = '')
    {
        $hash = hash('sha256', $key ?: $this->aesSecret, true);
        $enCrypt = openssl_encrypt($content, 'AES-256-CBC', $hash, PKCS7_TEXT, $this->aesHexIv($hex));
        return base64_encode($enCrypt);
    }


    /**
     * AES解密
     * @param string $content
     * @param string $key
     * @param string $hex
     * @return string
     */
    public function aesDecrypt($content = '', $key = '', $hex = '')
    {
        $content = base64_decode(str_replace(' ', '+', $content));
        $hash = hash('sha256', $key ?: $this->aesSecret, true);
        return openssl_decrypt($content, 'AES-256-CBC', $hash, PKCS7_TEXT, $this->aesHexIv($hex));
    }
}

https://www.php.net/manual/zh/openssl.pkcs7.flags.php

更多信息

https://baike.baidu.com/item/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86/468774?fromtitle=aes&fromid=5903&fr=aladdin

https://www.keylala.cn/aes

https://blog.csdn.net/GlatChen/article/details/79978875

https://suijimimashengcheng.51240.com/

进一步了解 aes 加密解密

在多端配合的情况下,不是 keyiv 都对,就可以解密出加密的数据的。 秘钥位数,加密模式,填充方式也得一一对应上。

aes 参数

  1. key length(密钥位数,密码长度)
  2. key (密钥,密码)
  3. IV (向量)
  4. mode (加密模式)
  5. padding (填充方式)