> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 正确处理签名 URL 的输入

> 通过限时链接的实现示例，讲解 Laravel 13 签名 URL 保护的范围，以及为什么要用 query() 获取已签名的查询值。

## 本页目的

在限时分享链接中，如果希望直接使用签发时确定的条件，除了验证 URL 签名之外，还需要统一**从哪里获取值**。
本页以一个示例进行说明：在 URL 中包含报表类型和输出格式，并返回用于预览的 JSON。

本页适用于 Laravel 13。URL 生成的整体介绍请参阅 [URL 生成](/zh-CN/urls)，获取输入的基础知识请参阅 [HTTP 请求](/zh-CN/requests)。

<Warning>
  签名并不保护整个请求。表单和 JSON 请求体不在签名范围内。
  已签名的查询值应在签名验证之后，通过 `$request->query('format')` 这样的方式获取。
  使用 `input()`、`all()` 或动态属性时，可能会获取到未签名的请求体中的值。GET 请求同样需要做此区分。
</Warning>

## 签名保护的范围

| 组成部分 | 处理方式 |
| - | - |
| URL 路径 | 纳入签名验证 |
| 查询字符串 | 原则上纳入签名验证，但从验证中排除的参数除外 |
| 协议和主机 | 通常包含在签名中，相对签名时不包含 |
| 表单和 JSON 请求体 | 不在签名范围内 |
| 有效期 | 由 `temporarySignedRoute()` 添加，验证时检查是否过期 |

`input()` 不仅处理查询字符串，也处理请求体。Laravel 在合并这些输入时，请求体中的值优先于查询字符串中的值。
而 `query()` 只获取查询字符串中的值。

签名验证成功，与之后读取的值是否已签名，是两件需要分别确认的事情。

```mermaid theme={null}
flowchart TD
    A["通过限时 URL 访问"] --> B{"signed 中间件<br>验证签名和有效期"}
    B -->|无效| C["403 响应"]
    B -->|有效| D["通过 query()<br>获取已签名的查询值"]
    D --> E["校验值的格式和允许范围"]
    E --> F["返回预览"]
```

## 实现限时预览链接

### 1. 定义进行验证的路由

在 `routes/web.php` 中定义一个必须带签名的命名路由。
这里不操作数据库或文件，只返回接收到的条件。

```php theme={null}
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Validator;

Route::get('/reports/preview', function (Request $request) {
    $options = Validator::make([
        'report' => $request->query('report'),
        'format' => $request->query('format'),
    ], [
        'report' => ['required', 'string', 'in:monthly,annual'],
        'format' => ['required', 'string', 'in:csv,pdf'],
    ])->validate();

    return response()->json($options);
})->middleware('signed')->name('reports.preview');
```

* `signed` 中间件会在闭包执行前验证签名和有效期。若无效，则返回 `403`。
* 使用 `query()` 从查询字符串中显式获取处理所需的值。
* 校验针对获取到的数组进行。签名验证并不保证值的类型或业务上允许的范围。

<Info>
  本示例中传给 `Validator::make()` 的只有通过 `query()` 取出的值。
  如果在用于校验已签名查询值的地方改用 `$request->all()`，就会处理到包含请求体在内的输入。
</Info>

### 2. 在可信的签发流程中生成 URL

注册命名路由后，在签发链接的处理中使用以下代码。
`report` 和 `format` 不是路径占位符，因此会被添加到查询字符串中。

```php theme={null}
use Illuminate\Support\Facades\URL;

$url = URL::temporarySignedRoute(
    'reports.preview',
    now()->addMinutes(30),
    ['report' => 'monthly', 'format' => 'csv'],
);
```

该 URL 除了指定的条件外，还包含 `expires` 和 `signature`。
`expires` 和 `signature` 是 Laravel 使用的保留参数，请不要将它们用作业务参数名。

在实际应用中公开签发流程时，不要让任何人都能以任意条件签发链接，并进行必要的[认证](/zh-CN/authentication)和[授权](/zh-CN/authorization)。

### 3. 确认正常使用

在有效期内直接打开签发的 URL，会返回以下 JSON。

```json theme={null}
{
  "report": "monthly",
  "format": "csv"
}
```

链接过期后，路由上的 `signed` 中间件会返回 `403`。
签名 URL 并不是一次性令牌。在本示例中，有效期内可以重复使用同一个 URL。
如果只允许使用一次，需要在签名之外另行管理已使用状态。

## 区分从验证中排除的值

如果希望允许前端追加的页码等参数，可以通过 `hasValidSignatureWhileIgnoring(['page'])` 将其从签名验证中排除。
但被排除的值任何人都可以修改，因此不要将其视为已签名的条件。

例如，用于显示的 `page` 与签发时固定的 `report`、`format` 作用不同。
不要仅仅为了方便，就排除那些用于判断目标数据、接收者、费用等的值。

<Warning>
  上面的实现示例使用的是普通的 `signed` 中间件，因此 `page` 也没有被排除。
  如果需要追加查询参数，请先确定生成端和验证端分别将哪些内容纳入签名。
</Warning>

## 使用相对签名时

在签名中不包含域名的设计中，需要组合使用生成时的 `absolute: false` 和验证时的 `signed:relative`。

```php theme={null}
$url = URL::temporarySignedRoute(
    'reports.preview',
    now()->addMinutes(30),
    ['report' => 'monthly', 'format' => 'csv'],
    absolute: false,
);
```

此时，将前面路由中的 `middleware('signed')` 改为 `middleware('signed:relative')`。
即使使用相对签名，请求体也不会被签名，因此通过 `query()` 获取值的方针不变。

## 实现检查清单

* 接收路由强制要求签名验证。
* 通过 `query()` 获取已签名的查询值。
* 另行校验值的格式和允许范围。
* 没有将从签名验证中排除的值当作签发时固定的条件使用。
* 生成端与验证端的绝对签名、相对签名设置保持一致。
* 没有将知道 URL 等同于已登录用户的权限。

## 相关页面与一手资料

* [URL 生成](/zh-CN/urls)：签名 URL 的 API 以及对无效签名的响应
* [HTTP 请求](/zh-CN/requests)：获取输入值和查询字符串
* [校验](/zh-CN/validation)：校验输入格式和允许的值
* [Laravel 官方文档：Signed URLs](https://github.com/laravel/docs/blob/13.x/urls.md#signed-urls)
* [Laravel 官方文档：Retrieving input from the query string](https://github.com/laravel/docs/blob/13.x/requests.md#retrieving-input-from-the-query-string)
* [Laravel Framework：UrlGenerator](https://github.com/laravel/framework/blob/13.x/src/Illuminate/Routing/UrlGenerator.php)
* [Laravel Framework：ValidateSignature](https://github.com/laravel/framework/blob/13.x/src/Illuminate/Routing/Middleware/ValidateSignature.php)


## Related topics

- [URL 生成](/zh-CN/urls.md)
- [Basic client - Laravel Bluesky](/zh-CN/packages/laravel-bluesky/basic-client.md)
- [事件与监听器](/zh-CN/events.md)
- [HTTP 请求](/zh-CN/requests.md)
- [Session](/zh-CN/session.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.