Laravel 对 ID 进行对称加密的 Composer 包 hashid

GitHub:
jiaxincui/hashid

介绍#

一个对 Laravel 应用模型 ID 进行对称加密的辅助函数。

依赖于 hashids/hashids

只适用于正整数加密。

安装#

$ composer require jiaxincui/hashid

配置#

  1. 复制 config/hashid.php 文件到 Laravel 项目的 config 文件夹。
  2. 在.env 文件添加配置项 HASH_ID_ALPHABET=your-key
    • 为了 Hash 成更安全的字符串,请手动重新生成 HASH_ID_ALPHABET,为 0-9a-zA-Z 共 62 个字符随机排序,字符不可重复,长度为 16-62,可使用以下方法生成
      echo str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');

      简单使用#

      包含两个辅助方法 id_encode()id_decode()
      在 Laravel 项目的任何地方均可使用这两个函数对 ID 进行加密或解密。

例子#


echo id_encode(4568); //输出:N5lkv0

echo id_decode('N5lkvO'); //输出:4568

//不可对float类型数字加密,不可对负数加密,给定任何非正整数参数都会抛出错误,如:
echo id_encode(2.36); //非正整数,抛出错误
echo id_encode(-23); //非正整数,抛出错误

//解密时任何无效字符串参数或校验错误都会抛出错误, 如:
echo id_decode('m_Dl9'); //包含无效字符,抛出错误
echo id_decode('nlK8GhRW'); //校验错误,抛出错误

hashids/hashids 不同的是 hashids 提供多个数字或包含多个数字的数组加密成一个字符串,
解密时以数组形式返回,这在实际应用中并不常见,反而给使用带来一定麻烦,
而此包对此进行了一些处理,不提供多个数字或多个数字数组的加密,解密时直接返回解密后的数字。

Laravel 深度应用#

加密#

有 2 种方法实现自动加密

  • 如果模型主键为 id

    通过 Hashid 提供的 trait,在数据库模型中使用 use Hashid;,对结果中的 id 字段自动加密成字符串,例如:

    
    <?php
    namespace App;

use Illuminate\Database\Eloquent\Model;
use Jiaxincui\Hashid\Traits\Hashid;

class User extends Model
{
use Hashid;

}

* 如果模型中的主键不是`id`

  你需要在模型中定义一个访问器,如你的主键为`pid`,在Model中添加访问器如下:

```php
public function getPidAttribute($value)
{
  return id_encode($value);
}

解密#

通过 Hashid 提供的 middleware 对路由参数解码,在控制器中无需做任何操作即可解码加密后的路由参数。
首先在 App\Http\Kernel.php 中注册中间件,在 Kernel 类的 $routeMiddleware 属性添加中间件条目。例如:

'hashid' => \Jiaxincui\Hashid\Http\Middleware\Hashid::class,

现在你可以在路由中分配中间件了。例如:

Route::resource('/users', 'UserController')->middleware('hashid');

中间件参数#

默认情况下,Hashid 中间件会解密当前路由的所有路由参数,如果你想指定被解密的路由参数可在中间件传入参数,例如:

Route::get('users/{user}/posts/{post}/comments/{comment}', function ($user, $post, $comment) {
    //
})->middleware('hashid:user,post');

以上例子中间件只解密给出的参数,如以上例子会解密路由参数 userpost, 不会解密 commnent

现在你的应用已经具备完整的加密和解密模型 ID 的功能。

本作品采用《CC 协议》,转载必须注明作者和本文链接
jenkincei
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 14

感谢分享。

github 上已经 有轮子 了,结合自己实际体验,觉着可以做一个 middleware,因为往往在数据库操作的时候需要原始 ID。

7年前 评论

这个不错,一般电商之类的比较在意这个,因为会泄漏订单数量什么的。

7年前 评论
jenkincei

@yansongda 啊,真的有呀,花了几个小时重复造了个轮子。

7年前 评论

你那个项目删了?打不开了。我自己搞的 int2string 估计跟你的差不多,也有几个现成的轮子:

@yansongda 也提到了,数据库操作时不方便,我现在是定义了 route model 。准备抽空写个专门处理 hashid 的轮子,管理各种 hashid ,以及零成本给 model 配置 hashid 。一直没腾出时间,也没找到现有的轮子..

7年前 评论
jenkincei

@ElfSundae 重新提交了项目已更新文章,看到了你说的两个轮子,思路大致相同,使用时确实有些不方便,暂时想到的是读取时可在 model 里利用访问器加密 id,像这样

public function getIdAttribute($value)
    {
        return id_encode($value);
    }

操作数据库还需要这样

public function show($id)
    {
        $id = id_decode($id));
    }

@yansongda 的思路是在操作数据库时候能否使用 middleware,暂时还没好的方案

7年前 评论

middleware 一般是针对 request 和 response 的,@yansongda 的意思可能是解析 URL 中的 hashid 然后 decode

7年前 评论
jenkincei

@ElfSundae 确实想到了一些思路,在 middleware 里确实可以获取到路由参数

$router = Route::current(); //获取当前路由实例
$id = id_decode($router->parameter('id')); //获取当前路由实例id参数并解密
$router->setParameter('id', $id); //设置当前路由的参数id为解密后的数字
7年前 评论

以前用 Golang 写过一个 Base62 编码的,应该类似。

http://www.cnblogs.com/qufo/p/5730229.html

7年前 评论
jenkincei

@ElfSundae 现在 1.1.0 发布,可以深度应用了,加密 trait 和解密 middleware 添加,感谢 @ElfSundae @yansongda 提供思路

7年前 评论
Jourdon

这个不错,如果可以确认生成的加密字符串不会重复的话,其实可以替代 uuid 了。

7年前 评论

@jenkincei 你定义了 getIdAttribute 但不一定所有主键都叫 id , 参考 Model 的 getKey()getRouteKey()

7年前 评论
jenkincei

@ElfSundae 谢谢提醒,这个动态添加访问器实现起来过于复杂,暂时没有想到更好的办法,如果主键不是 id 只能手动添加访问器了。

7年前 评论

有一个思路,在 trait 里重载 getAttribute , 大概意思如下:

public function getAttribute($key)
{
    $value = parent::getAttribute($key);

    if ($value && $key === $this->getRouteKeyName()) {
        $value = decode($value);
    }

    return $value;
}
7年前 评论
if (! is_null($value) && $key == ...)
7年前 评论