piggly/url-file-signer

生成具有唯一参数和签名的URL,以防止无效访问。

1.0.3 2020-03-14 13:43 UTC

This package is auto-updated.

Last update: 2024-09-14 23:48:14 UTC


README

Latest Version on Packagist Software License

本包受spatie/url-signer和Facebook图像URL架构启发。它可以创建具有有限生命周期的文件URL和签名检查器。但,它还包括一些功能,例如

  1. 隐藏文件路径,跨编码;
  2. 签名URL查询字符串;
  3. 附加文件参数。

使用此库,您的文件路径 /path/to/file/image.jpg 将转换为类似 https://cdn.example.com/564463774e6a45334e445934556a63304e6b5a584e6a59324f545a444e6a553d/image.jpg?oe=5DE6B01A&oh=22465b117a955a23728f306e3707eea5 的内容。

安装

此包可以通过 Composer 安装

composer require piggly/url-file-signer

URL结构

默认情况下,所有已签名的文件URL都将包含以下结构

  • baseUrl 基础主机/域名/URL;
  • /[fileParameter]/... (可选) 一个或多个文件参数;
  • /[encodedPath] 文件路径编码;
  • /[file.ext]? 文件名;
  • op=[orderOfParameters] (可选) 包含文件名中参数的顺序。
  • od=[domain] (可选) 域名所有者。
  • oe=[expiration] 编码的过期日期。
  • oh=[signature] 签名散列。

使用方法

签名对象可以签名和验证文件URL。使用唯一和秘密的密钥生成签名。非常简单

use Piggly\UrlFileSigner\FileSigner;

// Starting with a base url and the secret key
$fileSigner = FileSigner::create( 'https://cdn.example.com', 'mysecretkey' );

自定义查询字符串参数

签名对象允许自定义以下查询字符串参数

  1. 参数顺序:默认使用op。当文件有参数时,将包含一个编码的列表,其中包含按文件名中出现的顺序排列的参数;
  2. 过期时间:默认使用oe。将包含一个带有过期日期的编码timestamp
  3. 签名:默认使用oh。将包含一个附加到URL的hash字符串签名;
  4. 域名:默认使用od。但,您必须通过使用enableDomainParam()方法启用它。它检查URL主机提供者中的域名是否相同。

要自定义每个参数,请按照以下方式操作

// Change "Order of Parameters" parameter name
$fileSigner->changeOrderOfParametersParam('par');
// Change Expiration parameter name
$fileSigner->changeExpirationParam('exp');
// Change Signature parameter name
$fileSigner->changeSignatureParam('sig');
// Enable and set Domain parameter name
$fileSigner->enableDomainParam('dom');

设置文件参数

在大多数现代系统中,在保存文件时,算法可能会为文件生成许多不同的版本。以下是我们对文件参数的理解

修改文件的唯一信息。例如:大小、压缩、版本等。

要检测参数,签名对象将在文件名中查找并提取此参数以进行pos格式化。

让我们假设一个场景,其中同一个image.jpg有不同的sizes。然后,图像需要在其名称中设置属性,例如images_s250.jpgimages_s840.jpg等。

_是我们称为**文件分隔符**的内容。它将一个或多个参数(例如_s250_vprivate_c80)分开。而svc是我们称为**参数标识符**的内容。它们是识别下一个字符(例如/([a-z0-9]+)?/i)作为**参数值**的别名。所有参数都是文件名中的可选值。如果不希望捕获参数,请跳过本节。

文件实体

为了维护文件分隔符参数标识符参数值,在版本1.0.1中创建了一个File类。在使用签名对象并签名URL之前,您需要创建一个包含所有与您的文件相关的数据的File类,并将其分配给一个ParameterDict。如下所示:

use Piggly\UrlFileSigner\Collections\ParameterDict;
use Piggly\UrlFileSigner\Entities\File;

// Images allowed parameters
$imagesDict = ParameterDict::create()->add('version')->add('size')->add('compression');

// Create a file entity
$file = File::create( $imagesDict )->set('/path/to/file/image.jpg');

自定义文件分隔符

文件实体允许您将默认文件分隔符_更改为您想要的任何内容。只需使用

// Now, file separator will be '__'
$file->changeSeparator('__');

识别参数

为了使文件实体能够识别文件参数,将需要创建一个ParameterDict实例。这是一个管理参数标识符和别名的集合。首先,创建一个包含所有允许的文件参数的ParameterDict,将其放在文件名中

// [Tip] You can create a ParameterDict for each file type
$imageDict = ParameterDict::create()->add('version')->add('size')->add('compression');
$pdfDict   = ParameterDict::create()->add('version');

将ParameterDict类视为“允许的参数列表以及它们是如何组织的”。

参数标识符有一个与其相关的唯一字面名称和一个用于在文件名中查找的别名。参数字典将自动根据参数字面名称的第一个字母生成别名。但是,您可以自定义别名。

// Files name will contain _c([a-z0-9]+)? parameter
$imageDict->add('compression');

// Files name will contain _xx([a-z0-9]+)? parameter (using xx as alias)
$imageDict->add('contrast', 'xx');

创建参数字典后,在创建文件实体时将其关联

// Create a file entity including a ParameterDict class
$file = File::create( $imagesDict )->set('/path/to/file/image.jpg');

在参数字典中对参数进行排序

默认情况下,文件实体将按照您在参数字典中添加文件参数的顺序查找文件名并生成URI模式。但有时,您需要更改参数的顺序以使事情更有趣。

文件实体中有两种方法可以做到这一点。您可以在调用任何文件名或URL生成器之前随时调用它们。要排序URL模式中参数的显示方式,请调用sortToDisplay()方法。如下所示:

// It will show first the version parameter
$file->sortToDisplay(['version']);

// It will show first the version parameter, later size parameter
$file->sortToDisplay(['version','size']);

并且,要排序文件实体生成文件名所需的顺序,请调用sortInFileName()方法。如下所示:

// In file name the version parameter cames first
$file->sortInFileName(['version']);

// In file name the size parameter cames first, then cames the version parameter
$file->sortInFileName(['size','version']);

以下是一个现实世界的示例

// Setting all allowed parameters to image files
$imagesDict = ParameterDict::create()
    			->add('brightness')
    			->add('compression','x')
    			->add('contrast')
    			->add('version')
    			->add('size');

// The file name structure will be: file_b50_x82_c50_v1_s1080.jpg
// The generated URL paths will be: /b50/x82/c50/v1/s1080/...

// The File Entity can recognize these parameters
$file = File::create( $imagesDict );

// You changed the URL display order
$file->sortToDisplay( ['version','size','compression'] );
// You changed the file name order
$file->sortToDisplay( ['brightness','contrast','compression','size'] );

// The new file name structure will be: file_b50_c50_x82_s1080_v1.jpg
// The new generated URL Schema will be: /v1/s1080/x82/b50/c50/...

所有文件参数都是可选的。这意味着您不必担心文件是否存在。您只需要关注是否需要正确排序显示URL和文件名中的顺序。文件实体将做剩下的工作。

向文件实体添加参数

要将参数值添加到文件实体中,操作非常简单:

// The parameters attribute is a ParameterCollection class
$file->parameters->add( 'size', 1080 )->add( 'version', 1080 );

parameters属性,一个ParameterCollection类,有以下方法:

  • 一个公共的allowed属性,用于操作与File关联的ParameterDict类;
  • delete($name)用于删除参数。将返回\self
  • replace($name, $value)用于替换参数值。将返回\self
  • get($name)用于获取参数值。将返回参数值;
  • onlyParams($names)将以一个array返回仅包含$names的参数;
  • params()将以一个array返回所有参数;
  • paramsToFileName()将以一个array返回形成文件名的参数;
  • paramsToDisplay()将以一个array返回形成URL Schema中显示的参数;
  • valueExists($name)将检查是否存在值;
  • names()values()将仅返回参数名称或参数值;
  • count()将返回参数的数量。

文件实体方法

文件实体有许多有用的方法,其中最常见的是以下方法:

  • changeSeparator($separator)将更改默认文件分隔符;
  • getName($name)getExtension($ext)getPath($path)将分别获取文件名、扩展名和路径;
  • getFileName()将形成并返回带有路径和扩展名的完整文件名;
  • getFileNameEncoded()将形成并返回带有编码路径和扩展名的完整文件名。
  • getFileNameDecoded() 将生成并返回带有解码路径和扩展名的完整文件名。使用此函数时,需要将文件路径进行编码;
  • set($fileName) 将设置文件的名称、扩展名和路径;
  • setName($name)setExtension($ext)setPath($path) 分别用于设置文件的名称、扩展名和路径;
  • setRandomName() 将通过使用时间戳和随机函数创建一个唯一且数字的随机文件名。生成的模式将为 [0-9]{8,}_[0-9]{15,}_[0-9]{19}
  • sortToDisplay($newSort) 将对要显示在 URL Scheme 中的参数进行排序;
  • sortInFileName($newSort) 将对要插入到文件名中的参数进行排序。

其他所有方法将由已签名的对象自动使用。

生成签名 URL

签名 URL 由签名字符对象创建和验证。您可以通过向 sign() 方法提供 文件实体 和以 DateInterval 格式表示的 有效时间 来生成签名。

use Piggly\UrlFileSigner\Collections\ParameterDict;
use Piggly\UrlFileSigner\Entities\File;
use Piggly\UrlFileSigner\FileSigner;

// A parameter dictionary for images files
$imageDict = ParameterDict::create()->fill(['version', 'size', 'compression' => 'x']);

// A file with parameters size => 150
$file = File::create( $imagesDict )
				->set('/path/to/file/image.jpg')
				->parameters->fill(['size'=>150]);
				
// The generated URL will be encoded, signed and valid for 6 months
$signedUrl = FileSigner::create( 'https://cdn.example.com', 'mysecretkey' )->sign( $file, new DateInterval('P6M') );
// => https://cdn.example.com/s150/566a63774e6a45334e445934556a63304e6b5a554e6a59324f545a444e6a553d/image.jpg?op=cw&oe=5ECD709F&oh=8c569aa017a8b521afb7bf2c187d9089

在签名 URL 时,您可以发送查询字符串,如下所示:

// The generated URL will be encoded, signed and valid for 6 months. It also contains the query string `pid`. All sent query strings will be signed.
$signedUrl = FileSigner::create( 'https://cdn.example.com', 'mysecretkey' )->sign( $file, new DateInterval('P6M'), ['pid' => '309u5fj32958ikd' ] );

验证签名 URL

要验证签名 URL,只需调用 validate() 方法。如果 URL 无效,它将返回 false,或者返回一个包含挂载的文件名和 timestamparray

$data = FileSigner::create( 'https://cdn.example.com', 'mysecretkey' )->validate('https://cdn.example.com/s150/566a63774e6a45334e445934556a63304e6b5a554e6a59324f545a444e6a553d/image.jpg?op=cw%3D%3D&oe=5ECD709F&oh=8c569a-INVALID-afb7bf2c187d9089');
// => false

$imagePath = FileSigner::create( 'https://cdn.example.com', 'mysecretkey' )->validate('https://cdn.example.com/s150/566a63774e6a45334e445934556a63304e6b5a554e6a59324f545a444e6a553d/image.jpg?op=cw%3D%3D&oe=5ECD709F&oh=8c569aa017a8b521afb7bf2c187d9089');
// => [ 'file' => '/path/to/file/image_s150.jpg', 'exp' => 1590522015 ]

您可以使用 exp 来设置 HTTP Header Expires,您可以使用 file 来读取并将文件返回给浏览器。

提示

为了改善您查看此库的方式,我们在此分享一些有用的提示。让我们看看

  1. 如果您不想在 URL 中包含编码的文件路径。不用担心,只需发送不带路径的文件名即可;
  2. 编码的文件路径将返回一个长字符串。几乎比原始路径字符串大六倍。您可以考虑在 URL 中发送路径别名,然后在验证后,在数据库中查找原始文件路径。

未来可能将添加到 文件实体 中更好地管理路径的方法。

自定义签名者

此软件包提供了一个使用 MD5 哈希生成签名的签名者。您可以通过实现 interface Piggly\UrlFileSigner\BaseSigner 来创建自己的签名者。如果您让您的签名者扩展 Piggly\UrlFileSigner\UrlSigner,则只需提供 createSignature 方法。

未来实现

目前,我们知道编码的文件路径不是更好的方法,因为最终它返回一个非常长的字符串,从而生成一个非常长的 URL。在未来的实现中,我们将引入一种新的方法来做这件事。请随意贡献并解决这个问题。

记住

变更日志

有关最近更改的更多信息,请参阅 CHANGELOG

测试

此库使用 PHPUnit

vendor/bin/phpunit

贡献

有关详细信息,请参阅 CONTRIBUTING

安全

如果您发现任何与安全相关的问题,请通过电子邮件 dev@piggly.com.br 而不是使用问题跟踪器。

鸣谢

支持我们

Piggly Studio 是一家位于巴西里约热内卢的代理机构。

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件