控制器过滤器
控制器过滤器允许你在控制器执行之前或之后执行操作。与 事件 不同,你可以选择应用过滤器的特定 URI。传入过滤器可以修改请求,而后置过滤器可以操作甚至修改响应,提供了很大的灵活性和能力。使用过滤器可以执行的一些常见任务示例:
对传入请求执行 CSRF 保护
根据角色限制站点的区域访问
对某些端点执行速率限制
显示“维护中”页面
执行自动内容协商
等等…
创建过滤器
过滤器是简单的类,实现了 CodeIgniter\Filters\FilterInterface
。它们包含两个方法:before()
和 after()
,这些方法分别包含在控制器之前和之后运行的代码。你的类必须包含这两个方法,但如果不需要可以留空。过滤器骨架类如下:
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class MyFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
// Do something here
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Do something here
}
}
Before 过滤器
替换请求
在任何过滤器中,你都可以返回 $request
对象,它将替换当前的请求,允许你进行更改,这些更改在控制器执行时仍然存在。
停止后续过滤器
当你有一系列过滤器时,你可能还希望在某个过滤器后停止后续过滤器的执行。你可以通过返回任何非空结果轻松地实现这一点。如果 before 过滤器返回空结果,仍将执行控制器操作或后续过滤器。
非空结果规则的一个例外是 Request
实例。在 before 过滤器中返回它不会停止执行,只会替换当前的 $request
对象。
返回响应
由于 before 过滤器是在执行控制器之前执行的,所以有时你可能希望停止控制器中的操作。
这通常用于执行重定向,如下面的示例:
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class MyFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$auth = service('auth');
if (! $auth->isLoggedIn()) {
return redirect()->to(site_url('login'));
}
}
}
如果返回 Response
实例,将向客户端发送响应,并停止脚本执行。这对于实现 API 的速率限制很有用。请参见 Throttler 以获取示例。
After 过滤器
After 过滤器与 Before 过滤器几乎完全相同,只是你只能返回 $response
对象,并且无法停止脚本执行。这确实允许你修改最终输出,或者只是做一些最终输出的事情。这可以用于确保某些安全头正确设置,缓存最终输出,或者使用禁用词过滤器过滤最终输出。
配置过滤器
创建过滤器后,你需要配置它们的运行时机。这是在 app/Config/Filters.php 中完成的。该文件包含四个属性,允许你配置过滤器的确切运行时机。
警告
建议你在过滤器设置中的 URI 末尾始终添加 *
。因为控制器方法可能比你想象的通过不同的 URL 访问。例如,当启用 自动路由(传统) 时,如果你有 Blog::index
,它可以通过 blog
、blog/index
和 blog/index/1
等方式访问。
$aliases
$aliases
数组用于将简单名称与一个或多个完全限定的类名相关联,这些类名是要运行的过滤器:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public array $aliases = [
'csrf' => \CodeIgniter\Filters\CSRF::class,
];
// ...
}
别名是强制性的,如果你稍后尝试使用完整的类名,系统将抛出错误。以这种方式定义使得切换使用的类变得简单。非常适合当你决定需要更改到不同的身份验证系统时,因为你只需要更改过滤器的类即可完成。
你可以将多个过滤器组合成一个别名,使复杂的过滤器组非常简单:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public array $aliases = [
'apiPrep' => [
\App\Filters\Negotiate::class,
\App\Filters\ApiAuth::class,
],
];
// ...
}
你应该根据需要定义尽可能多的别名。
$globals
第二部分允许你定义任何应用于框架的每个请求的过滤器。在这里使用太多可能会对性能产生影响,所以要小心。可以通过将别名添加到 before 或 after 数组来指定过滤器:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'csrf',
],
'after' => [],
];
// ...
}
除了少数 URI
有时你希望将过滤器应用于几乎所有请求,但有一些应该不受影响。一个常见的示例是,如果你需要从 CSRF 保护过滤器中排除几个 URI,以允许第三方网站的请求访问一个或两个特定的 URI,同时保持其余 URI 受保护。要做到这一点,请在别名旁边添加一个包含 except
键和要匹配的 URI 值的数组:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'csrf' => ['except' => 'api/*'],
],
'after' => [],
];
// ...
}
在过滤器设置中可以使用 URI 的任何位置,你都可以使用正则表达式,或者像在这个例子中使用星号 (*
) 作为通配符,匹配之后的所有字符。在这个例子中,任何以 api/
开头的 URL 都将被免于 CSRF 保护,但网站的表单将全部受保护。如果你需要指定多个 URI,可以使用 URI 模式数组:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'csrf' => ['except' => ['foo/*', 'bar/*']],
],
'after' => [],
];
// ...
}
$methods
警告
如果使用 $methods
过滤器,你应该 禁用自动路由(传统),因为 自动路由(传统) 允许任何 HTTP 方法访问控制器。以你不期望的方法访问控制器可能会绕过过滤器。
你可以将过滤器应用于所有某种 HTTP 方法的请求,如 POST、GET、PUT 等。在此数组中,你需要以**全小写**指定方法名称。它的值将是要运行的过滤器数组:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $methods = [
'post' => ['foo', 'bar'],
'get' => ['baz'],
];
// ...
}
备注
与 $globals
或 $filters
属性不同,这些只能作为 before 过滤器运行。
除了标准的 HTTP 方法外,这也支持一个特殊情况:cli
。cli
方法将应用于所有从命令行运行的请求。
$filters
该属性是一个过滤器别名数组。对于每个别名,你可以为 before
和 after
数组指定过滤器应该应用到的一系列 URI 模式:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $filters = [
'foo' => ['before' => ['admin/*'], 'after' => ['users/*']],
'bar' => ['before' => ['api/*', 'admin/*']],
];
// ...
}
过滤器参数
在配置过滤器时,可以在设置路由时向过滤器传递其他参数:
<?php
$routes->add('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);
在这个例子中,数组 ['dual', 'noreturn']
将在过滤器的 before()
和 after()
实现方法的 $arguments
中传递。
确认过滤器
CodeIgniter 提供了以下 命令 来检查路由的过滤器。
filter:check
4.3.0 新版功能.
使用 GET 方法检查路由 /
的过滤器:
> php spark filter:check get /
输出如下所示:
+--------+-------+----------------+---------------+
| Method | Route | Before Filters | After Filters |
+--------+-------+----------------+---------------+
| GET | / | | toolbar |
+--------+-------+----------------+---------------+
你还可以通过 spark routes
命令查看路由和过滤器。
参见 URI 路由。
提供的过滤器
CodeIgniter4 提供的过滤器有: Honeypot、CSRF、InvalidChars
、SecureHeaders
和 DebugToolbar。
备注
过滤器按配置文件中定义的顺序执行。但是,如果启用, DebugToolbar
总是最后执行,因为它应该能够捕获其他过滤器中发生的所有事情。
InvalidChars
此过滤器禁止用户输入数据($_GET
、$_POST
、$_COOKIE
、php://input
)包含以下字符:
无效的 UTF-8 字符
除换行和制表符之外的控制字符
SecureHeaders
此过滤器添加 HTTP 响应头,你的应用程序可以使用它们来提高应用程序的安全性。
如果要自定义头,请扩展 CodeIgniter\Filters\SecureHeaders
并覆盖 $headers
属性。并在 app/Config/Filters.php 中更改 $aliases
属性:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public array $aliases = [
// ...
'secureheaders' => \App\Filters\SecureHeaders::class,
];
// ...
}
如果你想了解安全头,请参阅 OWASP 安全头项目。