今天对博客的数据ID进行了一次优化, 优化效果如图:
优化前:
优化后:
效果显而易见, 普通项目中, 一般使用的项目ID通常为数据库的自增ID, 但是自增的ID有一些问题:
- 数据的ID是暴露的
- 会被别有用心的人恶意采集
- 可以很容易的猜测出数据库中的数据个数
- 随着数据的增长, ID会越来越长, 直接显示在URL中会十分的突兀
开始我的想法是, 使用加密等手段自己生成ID, 将数据库中的新增ID映射字段并统一为自定义生成的ID, 但是这个方案对其他使用者十分不友好, 我需要给每个人解释一遍我数据库中的该字段是做什么的.
于是我开始百度其他人是怎么解决的, 了解到一个名为 vinkla/hashids 的包完美的解决了这个问题, 人生苦短, 既然有现成的轮子, 何必花时间自己写呢, 开工!
vinkla/hashids 是 hashids 在 Laravel 中的封装,hashids 是一个可以通过数字生成简短,唯一,无序字符串的开源库,它不仅能将数字转换为字符串,还能将转换后的结果转换回数字,利用这一点,我们可以很好的解决上述问题。
分析目前的问题:
- 将所有的文章 ID 由
/topics/1
修改为 hashids 转换后的值/topics/nNO31rpJx
, 所有涉及文章的地方都要更改. - 在查询时由于此时获取到的是一个哈希字符串, 需要先将这个字符串转回先前的ID在进行查询操作.
- 由于我才用的是 路由模型绑定 的方式获取文章, 需要重新实现一下模型路由绑定的中间件, 或者在所有使用模型绑定的地方都用自己的方法实现.
- 要考虑到代码的封装以及重用以便以后扩展业务, 不违背 DRY(Don't repeat yourself) 原则
即便是个不起眼的小优化, 但涉及的东西还是不少, 处理时还需谨慎
1. 首先安装扩展包:
在安装扩展包是需要注意, 由于我当前的 laravel 版本是 5.5, 如果我直接使用
$ composer require vinkla/hashids
会报一个vinkla/hashids 5.0.0 requires illuminate/contracts 5.6.*
的错, 大概意思是5.0.0版本vinkla/hashids
需要laravel 5.6 才能安装, 所以 5.6 以下版本在安装这个包是需要制定一下版本号
$ composer require vinkla/hashids:~3.3
2. 发布配置文件:
$ php artisan vendor:publish --provider="Vinkla\Hashids\HashidsServiceProvider"
更改配置如下
'default' => 'main',
'connections' => [
'main' => [
'salt' => 'larabbs',
'length' => '9',
],
'alternative' => [
'salt' => 'your-salt-string',
'length' => 'your-length-integer',
],
]
vinkla/hashids
的思路是将数字转换为字符串,同时又可以将字符串再转换回数字。为了保证字符串不被轻易的破解,我们需要配置盐(salt),也就是给目标数字额外增加一些变量,保证他人在没有相同盐(salt)的情况下,无法将符串再转换回数字,增加了安全性。同时我们还可以指定转换后字符串的最小长度,这样能保证 ID 尽量统一美观。
3. 创建一个 Trait 文件:
$ touch app/Models/Traits/HashIdHelper.php
填入如下内容:
app/Models/Traits/HashIdHelper.php
hash_id 时触发
public function getHashIdAttribute()
{
if (!$this->hashId) {
$this->hashId = Hashids::encode($this->id);
}
return $this->hashId;
}
// 先将参数 decode 为模型id,再调用父类的 resolveRouteBinding 方法
public function resolveRouteBinding($value)
{
if (!is_numeric($value)){
$value = current(Hashids::decode($value));
if (!$value) {
return;
}
}
return parent::resolveRouteBinding($value);
}
// 使用 hash_id 生成 URL
public function getRouteKey()
{
return $this->hash_id;
}
}
重写了 resolveRouteBinding
和 getRouteKey
两个方法。其中 getHashIdAttribute
是自定义的一个访问器,当访问模型的 hash_id 属性时会通过该方法返回数据,这样任何使用了这个 Trait 的 Eloquent 模型就都能直接使用 hashids
了。
4. 最后在Topic模型中使用这个 Trait:
use Traits\HashIdHelper;
大功告成!