控制器过滤器
控制器过滤器允许您在控制器执行之前或之后执行操作。与 事件 不同,您可以选择将过滤器应用于的特定 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
}
}
前置过滤器
替换请求
在任何过滤器中,您都可以返回 $request
对象,它将替换当前请求,允许您进行更改,这些更改在控制器执行时仍然存在。
停止后续过滤器
此外,当您有一系列过滤器时,您可能还想在某个过滤器之后停止执行后续过滤器。您可以通过返回 **任何非空** 结果轻松地做到这一点。如果前置过滤器返回空结果,则控制器操作或后续过滤器仍将执行。
非空结果规则的例外是 Request
实例。在前置过滤器中返回它不会停止执行,而只会替换当前的 $request
对象。
返回响应
由于前置过滤器在您的控制器执行之前执行,因此您有时可能希望停止控制器中的操作。
这通常用于执行重定向,例如在此示例中
<?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。
后置过滤器
后置过滤器与前置过滤器几乎相同,只是您只能返回 $response
对象,并且不能停止脚本执行。这允许您修改最终输出,或者只是对最终输出执行某些操作。这可以用于确保某些安全标头以正确的方式设置,或者缓存最终输出,甚至使用脏话过滤器过滤最终输出。
配置过滤器
在过滤器运行时,有两种方法可以配置它们。一种是在 app/Config/Filters.php 中完成,另一种是在 app/Config/Routes.php 中完成。
如果要将过滤器指定到特定路由,请使用 app/Config/Routes.php 并查看 URI 路由。
指定到路由(在 app/Config/Routes.php 中)的过滤器将在 app/Config/Filters.php 中指定的过滤器之前执行。
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 = [
'api-prep' => [
\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 路径(相对于 BaseURL)作为值。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $globals = [
'before' => [
'csrf' => ['except' => 'api/*'],
],
'after' => [],
];
// ...
}
在过滤器设置中可以使用 URI 路径(相对于 BaseURL)的任何地方,都可以使用正则表达式,或者像本例一样,使用星号 (*
) 作为通配符,它将匹配该字符之后的所有字符。在本例中,任何以 api/
开头的 URI 路径都将免受 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' => ['invalidchars', 'csrf'],
'get' => ['csrf'],
];
// ...
}
注意
与 $globals
或 $filters
属性不同,这些过滤器只会作为前置过滤器运行。
除了标准的 HTTP 方法之外,它还支持一个特殊情况:cli
。 cli
方法将应用于从命令行运行的所有请求。
$filters
此属性是一个过滤器别名数组。对于每个别名,您可以指定 before
和 after
数组,其中包含过滤器应应用到的 URI 路径(相对于 BaseURL)模式列表。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public array $filters = [
'foo' => ['before' => ['admin/*'], 'after' => ['users/*']],
'bar' => ['before' => ['api/*', 'admin/*']],
];
// ...
}
过滤器参数
版本 4.4.0 中的新功能。
在配置 $filters
时,可以向过滤器传递额外的参数。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
// ...
public $filters = [
'group:admin,superadmin' => ['before' => ['admin/*']],
'permission:users.manage' => ['before' => ['admin/users/*']],
];
// ...
}
在这个例子中,当 URI 匹配 admin/*'
时,数组 ['admin', 'superadmin']
将在 $arguments
中传递给 group
过滤器的 before()
方法。当 URI 匹配 admin/users/*'
时,数组 ['users.manage']
将在 $arguments
中传递给 permission
过滤器的 before()
方法。
确认过滤器
CodeIgniter 有以下 命令 来检查路由的过滤器。
filter:check
版本 4.3.0 中的新功能。
例如,检查路由 /
的过滤器,使用 GET 方法
php spark filter:check get /
输出如下
+--------+-------+----------------+---------------+
| Method | Route | Before Filters | After Filters |
+--------+-------+----------------+---------------+
| GET | / | | toolbar |
+--------+-------+----------------+---------------+
您也可以通过 spark routes
命令查看路由和过滤器,但当您对路由使用正则表达式时,它可能无法显示准确的过滤器。有关详细信息,请参阅 URI 路由。
提供的过滤器
CodeIgniter4 附带的过滤器有:蜜罐、CSRF、InvalidChars
、SecureHeaders
和 调试工具栏。
注意
过滤器按配置文件中定义的顺序执行。但是,如果启用,DebugToolbar
始终最后执行,因为它应该能够捕获其他过滤器中发生的所有事件。
InvalidChars
此过滤器禁止用户输入数据($_GET
、$_POST
、$_COOKIE
、php://input
)包含以下字符
无效的 UTF-8 字符
除换行符和制表符之外的控制字符
安全头信息
此过滤器添加 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 安全头信息项目。