为什么要保护数据库主键?

数据库主键一般是有序自增主键,极易被爬虫抓取数据,作为应用开发者,这是不应该的,你辛辛苦苦收集的数据转眼之间被其他人给抓取了,是不是很大的损失?

Hashids的介绍

generate short unique ids from integers

理解为数字编码库即可,几乎支持市面上所有语言。

available in JavaScript, Ruby, Python, Java, Scala, PHP, Perl, Perl 6, Swift, Clojure, Objective-C, C, C++11, D, F#, Go, Erlang, Lua, Haskell, OCaml, Elixir, Rust, Smalltalk, ColdFusion, Groovy, Kotlin, Nim, VBA, Haxe, Crystal, Elm, ActionScript, CoffeeScript, Bash, R, TSQL, PostgreSQL and for

PHP使用

$hashids = new Hashids\Hashids('this is my salt');
$id = $hashids->encode(1, 2, 3);
$numbers = $hashids->decode($id);

注意

该库并不是一个加密库,所以不建议用来加密敏感数据,我们的数据库主键ID并不是业务上的敏感数据,所以这个没关系。

Yii2的使用

由于该编解码是独立与业务之外的,所以需要处理的地方在下面:

  1. 接收请求数据的自动解码
  2. 响应数据的自动编码(本文只针对JSON响应处理,有需要的可以添加ResponseFormatter自行处理)

这两个步骤不应该提现在控制器中,控制器拿到的数据是解码好的,响应的数据是原始数据,然后我们在响应中处理。

代码

助手类(HashidsHelper)


class HashidsHelper {
public static function encode($id)
{
$hashids = new \Hashids\Hashids('salt',16);
return $hashids->encode($id);
} public static function decode($hash)
{
$hashids = new \Hashids\Hashids('salt',16);
$data= $hashids->decode($hash);
return empty($data)?null:$data;
} public static function decodeArray(array $hashes)
{
return array_map([HashidsHelper::class, 'decode'], $hashes);
}
/**
* 递归编码
* @param array $data
*/
public static function encodeRecursive(array &$data)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
self::encodeRecursive($value);
continue;
}
if (strpos($key, 'id') !== false && is_numeric($value)) {
$data[$key] = static::encode($value);
}
}
} /**
* 递归解码
* @param array $data
*/
public static function decodeRecursive(array &$data)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
self::decodeRecursive($value);
continue;
}
if (strpos($key, 'id') !== false) {
if (is_string($value)) {
$id = static::decode($value);
$data[$key] = $id ?? $value;
} elseif (is_array($value)) {
$data[$key] = static::decodeArray($value);
}
}
}
}
}

处理请求数据($_POST,$_PUT,$_GET)提交过来的数据

1.新建JsonParser继承Yii自带的JsonParser,代码如下


class JsonParser extends \yii\web\JsonParser
{
/**
* @inheritDoc
*/
public function parse($rawBody, $contentType)
{
$data = parent::parse($rawBody, $contentType);
if ($data !== null) {
HashidsHelper::decodeRecursive($data);
}
return $data;
}
}

2.新建Request集成Yii自带的Request,重写getQueryParams,代码如下:

    public function getQueryParams()
{
$data = parent::getQueryParams();
if ($data !== null) {
HashidsHelper::decodeRecursive($data);
}
return $data;
}

3.配置web.php的components,更改为我们自定义的处理器

        'request' => [
'class' => \app\components\Request::class,
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => '123456',
'enableCsrfValidation' => false,
'parsers' => [
'application/json' => \app\components\web\JsonParser::class
]
],

处理响应数据

1.新建JsonResponseFormatter继承Yii的JsonResponseFormatter,代码如下:

class JsonResponseFormatter extends \yii\web\JsonResponseFormatter
{
/**
* @inheritDoc
*/
public function format($response)
{
if ($response->data !== null) {
HashidsHelper::encodeRecursive($response->data);
}
parent::format($response);
}
}

2.配置web.php的components,替换response组件

        'response' => [
'class' => \app\components\web\Response::class,
'format' => Response::FORMAT_JSON,
'formatters' => [
'json' => [
'class' => \app\components\web\JsonResponseFormatter::class,
'prettyPrint' => YII_DEBUG
]
]
],

测试

1.SiteController添加方法

    public function actionA($corporation_id)
{
$data = Yii::$app->request->post();
var_dump($data, $corporation_id);
} public function actionB()
{
return [
'app_id' => 1,
'app' => [
'app_id' => 2
]
];
}

2.请求测试,这个加密过的hash读者可能解不开,因为我们用的salt不一样,替换为你自己的即可


POST /site/a?corporation_id=XaYeAV2q80pkB4KL {
"corporation_id": "XaYeAV2q80pkB4KL",
"applet":{
"id":"XaYeAV2q80pkB4KL",
"appid":"xxxxxx"
}
}

3.响应的内容如下:


array(2) {
["corporation_id"]=>
int(1)
["applet"]=>
array(2) {
["id"]=>
int(1)
["appid"]=>
string(6) "xxxxxx"
}
}
int(1)

4.响应测试


GET /site/b

5.响应内容如下



{
"app_id": "XaYeAV2q80pkB4KL",
"app": {
"app_id": "LOnMp3QR5lryDgRK"
}
}

写在最后

不知道这个算不算AOP编程?个人觉得算,在业务逻辑之外处理,业务层对外部输入和自身输出是透明的(理解为业务层自己不知道加解密)。

本文核心在于两个递归方法,其他语言类似,像nodejs可以使用中间件来处理。

原文地址:https://segmentfault.com/a/1190000015704969

使用Hashids来保护你的数据库主键的更多相关文章

  1. Statement和PreparedStatement的特点 MySQL数据库分页 存取大对象 批处理 获取数据库主键值

    1 Statement和PreparedStatement的特点   a)对于创建和删除表或数据库,我们可以使用executeUpdate(),该方法返回0,表示未影向表中任何记录   b)对于创建和 ...

  2. 数据库主键到底是用自增长(INT)好还是UUID好

    其实针对使用自增长还是UUID,大家讨论最多的就是速度和存储空间,这里我加入了安全性和分布式,具体对比如下: 使用自增长做主键的优点:1.很小的数据存储空间2.性能最好3.容易记忆使用自增长做主键的缺 ...

  3. SQL 数据库主键 ,外键

    主键 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它可强制表的实体完整性.当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键.一个 ...

  4. c#生成唯一编号方法记录,可用数据库主键 唯一+有序

    数据库主键目前主要有两种: a.自增数值型 优:占用空间小,插入快,有序对索引友好,易懂 缺:多数据库迁移会有重复键值问题,有可能爆表 b.GUID 优:多数据库唯一 缺:占用空间大,无序对索引不友好 ...

  5. SQL Server数据库主键与索引的几点区别

    我们在使用SQL Server数据库的时候常常会创建主键和索引,那么主键和索引到底有什么样的不同呢?本文我们主要介绍了主键和索引的区别. 主键和索引的区别如下: 主键是索引,但索引不一定是主键. 主键 ...

  6. MySQL数据库主键设计原则

    目录 1. 主键定义... 5 2. 主键设计原则... 5 2.1 确保主键的无意义性... 5 2.2 采用整型主键... 5 2.3 减少主键的变动... 5 2.4 避免重复使用主键... 6 ...

  7. 数据库主键跟外键+修改mysql的密码

    update myspl.user set password=PASSWORD(设置的密码)  where user='root'; 如果修改错误:先执行use mysple;再重复上面的代码. 一. ...

  8. SqlServer 开启或关闭数据库主键自增

    可用作删除一行主键数据库,在还原数据行,或者删掉后,被伤处的主键还可以利用 --开启当前表的可复制功能,仅在当前回话中有效 SET IDENTITY_INSERT  dbo.PDAUserInfo O ...

  9. 关于MySql数据库主键及索引的区别

    一.什么是索引?索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里 ...

随机推荐

  1. React 事件 传参

    绑定事件用 bind方法,第一个参数是 this,第二个参数是需要传入的值如下: <a onClick={this.updateLabel.bind(this,'参数1','参数2')} > ...

  2. 组合数的几种球阀 By cellur925

    先来了解几个概念:排列数,组合数. 一.定义及有用的性质 排列数:从n个不同元素中依次取出m个元素排成一列的方案数.P(n,m)=n!/(n-m)! 组合数:从n个不同元素中依次取出m个元素形成一个集 ...

  3. JDBC中的DriverManager.getConnection(url)中的参数url

    1.Oracle8/8i/9i数据库(thin模式) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); ...

  4. centos7静态网络配置

    centos7静态网络配置 cd /etc/sysconfig/network-scripts 找到当前网卡名字 vim ifcfg-ens33 TYPE="Ethernet" # ...

  5. 【洛谷4769】[NOI2018] 冒泡排序(动态规划_组合数学)

    题目: 洛谷 4769 博客页面左下角的嘴嘴瓜封神之战中的题目 分析: 一个排列交换次数为 \(\frac{1}{2}\sum_{i=1}^{n}|i-p_i|\) 的充要条件是这个排列不存在长度为 ...

  6. 51nod1183 编辑距离

    1183 编辑距离 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个 ...

  7. 对比度受限的自适应直方图均衡化(CLAHE)

    直方图均衡化(HE)是一种很常用的直方图类方法,基本思想是通过图像的灰度分布直方图确定一条映射曲线,用来对图像进行灰度变换,以达到提高图像 对比度的目的.该映射曲线其实就是图像的累计分布直方图(CDF ...

  8. Android偏好设置(4)设置默认值

    Setting Default Values The preferences you create probably define some important behaviors for your ...

  9. C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响)

    C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响),如以下代码将无法通过编译. foreach (int x in myArray) { x++; //错误代码,因为改变 ...

  10. Spring中bean的五个作用域简介(转载)

    Spring上个版本的IoC容器支持两个不同的bean作用域(单例与原型).Spring 2.0改进了这一点,不仅提供了一些依赖于Spring部署环境(比如说,在web环境中的request和sess ...