授权作为中间件应用于您的应用程序。 AuthorizationMiddleware
处理以下职责
使用装饰器装饰请求的 ‘identity’,该装饰器添加 can
、canResult
和 applyScope
方法(如果需要)。
确保请求中已检查/绕过授权。
要使用中间件,请在您的应用程序类中实现 AuthorizationServiceProviderInterface
。然后将您的应用程序实例传递到中间件,并将中间件添加到队列中。
一个基本示例是
namespace App;
use Authorization\AuthorizationService;
use Authorization\AuthorizationServiceProviderInterface;
use Authorization\Middleware\AuthorizationMiddleware;
use Authorization\Policy\OrmResolver;
use Cake\Http\BaseApplication;
class Application extends BaseApplication implements AuthorizationServiceProviderInterface
{
public function getAuthorizationService(ServerRequestInterface $request, ResponseInterface $response)
{
$resolver = new OrmResolver();
return new AuthorizationService($resolver);
}
public function middleware($middlewareQueue)
{
// other middleware
$middlewareQueue->add(new AuthorizationMiddleware($this));
return $middlewareQueue;
}
}
授权服务需要策略解析器。有关可用解析器以及如何使用它们的说明,请参阅 策略 文档。
默认情况下,请求中的 identity
将使用 Authorization\IdentityDecorator
进行装饰(包装)。装饰器类代理方法调用、数组访问和属性访问到装饰的身份对象。要直接访问底层身份,请使用 getOriginalData()
$originalUser = $user->getOriginalData();
如果您的应用程序使用 cakephp/authentication 插件,则将使用 Authorization\Identity
类。该类除了实现 Authorization\IdentityInterface
之外,还实现了 Authentication\IdentityInterface
。这使您可以使用 Authentication
库的组件和助手来获取装饰后的身份。
如果您有现有的 User
或身份类,您可以通过实现 Authorization\IdentityInterface
并使用 identityDecorator
中间件选项来跳过装饰器。首先,让我们更新我们的 User
类
namespace App\Model\Entity;
use Authorization\AuthorizationServiceInterface;
use Authorization\IdentityInterface;
use Authorization\Policy\ResultInterface;
use Cake\ORM\Entity;
class User extends Entity implements IdentityInterface
{
/**
* @inheritDoc
*/
public function can(string $action, mixed $resource): bool
{
return $this->authorization->can($this, $action, $resource);
}
/**
* @inheritDoc
*/
public function canResult(string $action, mixed $resource): ResultInterface
{
return $this->authorization->canResult($this, $action, $resource);
}
/**
* @inheritDoc
*/
public function applyScope(string $action, mixed $resource, mixed ...$optionalArgs): mixed
{
return $this->authorization->applyScope($this, $action, $resource, ...$optionalArgs);
}
/**
* @inheritDoc
*/
public function getOriginalData(): \ArrayAccess|array
{
return $this;
}
/**
* Setter to be used by the middleware.
*/
public function setAuthorization(AuthorizationServiceInterface $service)
{
$this->authorization = $service;
return $this;
}
// Other methods
}
现在我们的用户实现了必要的接口,让我们更新我们的中间件设置
// In your Application::middleware() method;
// Authorization
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'identityDecorator' => function ($auth, $user) {
return $user->setAuthorization($auth);
}
]));
您不再需要更改任何现有的类型提示,并且可以开始在您有权访问用户的地方使用授权策略。
如果您还使用 Authentication 插件,请确保实现这两个接口。
use Authorization\IdentityInterface as AuthorizationIdentity;
use Authentication\IdentityInterface as AuthenticationIdentity;
class User extends Entity implements AuthorizationIdentity, AuthenticationIdentity
{
...
/**
* Authentication\IdentityInterface method
*
* @return string
*/
public function getIdentifier()
{
return $this->id;
}
...
}
默认情况下,AuthorizationMiddleware
将确保每个包含 identity
的请求都已检查/绕过授权。如果未检查授权,则会引发 AuthorizationRequiredException
。此异常是在您的其他中间件/控制器操作完成后引发的,因此您不能依赖它来阻止未经授权的访问,但它在开发/测试期间是一个有用的帮助。您可以通过选项禁用此行为
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'requireAuthorizationCheck' => false
]));
默认情况下,应用程序抛出的授权异常由中间件重新抛出。您可以为未经授权的请求配置处理程序并执行自定义操作,例如将用户重定向到登录页面。
内置处理程序为
Exception
- 此处理程序将重新抛出异常,这是中间件的默认行为。
Redirect
- 此处理程序将请求重定向到提供的 URL。
CakeRedirect
- 支持 CakePHP 路由器的重定向处理程序。
这两个重定向处理程序共享相同的配置选项
url
- 要重定向到的 URL(CakeRedirect
支持 CakePHP 路由器语法)。
exceptions
- 应该重定向的异常类列表。默认情况下,只有 MissingIdentityException
被重定向。
queryParam
- 访问的请求 URL 将附加到重定向 URL 的查询参数(默认情况下为 redirect
)。
statusCode
- 重定向的 HTTP 状态代码,默认情况下为 302
。
例如
use Authorization\Exception\MissingIdentityException;
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'unauthorizedHandler' => [
'className' => 'Authorization.Redirect',
'url' => '/pages/unauthorized',
'queryParam' => 'redirectUrl',
'exceptions' => [
MissingIdentityException::class,
OtherException::class,
],
],
]));
所有处理程序都将抛出的异常对象作为参数传递。此异常始终是 Authorization\Exception\Exception
的实例。在这个示例中,Authorization.Redirect
处理程序只为您提供指定要侦听哪些异常的选项。
因此,在这个我们使用 Authorization.Redirect
处理程序的示例中,如果我们想优雅地处理它们,我们可以将其他基于 Authorization\Exception\Exception
的异常添加到 execeptions
数组中
'exceptions' => [
MissingIdentityException::class,
ForbiddenException::class
],
配置选项作为最后一个参数传递给处理程序的 handle()
方法。
目前没有直接的方法可以将闪存消息添加到未经授权的重定向中。
因此,您需要创建自己的处理程序,该处理程序添加闪存消息(或您希望在重定向时发生的任何其他逻辑)
创建此文件 src/Middleware/UnauthorizedHandler/CustomRedirectHandler.php
<?php
declare( strict_types = 1 );
namespace App\Middleware\UnauthorizedHandler;
use Authorization\Exception\Exception;
use Authorization\Middleware\UnauthorizedHandler\RedirectHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class CustomRedirectHandler extends RedirectHandler {
public function handle( Exception $exception, ServerRequestInterface $request, array $options = [] ): ResponseInterface {
$response = parent::handle( $exception, $request, $options );
$request->getFlash()->error( 'You are not authorized to access that location' );
return $response;
}
}
告诉 AuthorizationMiddleware 应该使用您的新的自定义处理程序
// in your src/Application.php
use Authorization\Exception\MissingIdentityException;
use Authorization\Exception\ForbiddenException;
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'unauthorizedHandler' => [
'className' => 'CustomRedirect', // <--- see here
'url' => '/users/login',
'queryParam' => 'redirectUrl',
'exceptions' => [
MissingIdentityException::class,
ForbiddenException::class
],
'custom_param' => true,
],
]));
如您所见,您仍然拥有与使用 Authorization.Redirect
作为 className 一样配置参数。
这是因为我们基于插件中提供的 RedirectHandler 扩展了我们的处理程序。因此,所有这些功能都存在,并且我们在 handle()
函数中拥有自己的功能。
如果您希望向您的功能添加更多配置参数,则 custom_param
会出现在您在 CustomRedirectHandler
中的 handle()
函数中提供的 $options
数组中。
您可以查看 CakeRedirectHandler 或 RedirectHandler 来了解这样的处理程序应该是什么样子。