从 AuthComponent 迁移

差异

  • 此插件有意 **不** 处理授权。它是为了明确的关注点分离,而被有意地从授权中解耦。另请参见计算机访问控制。此插件仅处理标识身份验证。我们可能会有另一个用于授权的插件。

  • 没有自动检查会话。要从会话中获取实际的用户数据,您需要使用 SessionAuthenticator。如果配置的会话键中存在数据,它将检查会话并将数据放入身份对象中。

  • 用户数据不再通过旧的 AuthComponent 可用,而是可以通过请求属性访问,并封装在身份对象中:$request->getAttribute('authentication')->getIdentity();。此外,您可以利用 AuthenticationComponent getIdentity()getIdentityData() 方法。

  • 身份验证过程的逻辑已拆分为验证器和标识符。验证器将从请求中提取凭据,而标识符则验证凭据并找到匹配的用户。

  • DigestAuthenticate 已重命名为 HttpDigestAuthenticator

  • BasicAuthenticate 已重命名为 HttpBasicAuthenticator

相似之处

  • 所有现有的身份验证适配器,Form、Basic、Digest 仍然存在,但已重构为验证器。

标识符和验证器

遵循关注点分离的原则,以前的身份验证对象被拆分为单独的对象,标识符和验证器。

  • **验证器** 接收传入的请求并尝试从中提取身份凭据。如果找到凭据,它们将被传递给一组标识符,这些标识符将定位用户。因此,验证器以 IdentifierCollection 作为第一个构造函数参数。

  • **标识符** 将身份凭据与存储系统进行验证。例如(ORM 表、LDAP 等)并返回已识别的用户数据。

这使得根据需要更改标识逻辑或使用多个用户数据源变得容易。

如果您想实现自己的标识符,您的标识符必须实现 IdentifierInterface

迁移您的身份验证设置

迁移应用程序的第一步是在应用程序的引导方法中加载身份验证插件

public function bootstrap(): void
{
    parent::bootstrap();
    $this->addPlugin('Authentication');
}

然后更新您的应用程序以实现身份验证提供程序接口。这可以让 AuthenticationMiddleware 知道如何从您的应用程序获取身份验证服务

// in src/Application.php

// Add the following use statements.
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

// Add the authentication interface.
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
    /**
     * Returns a service provider instance.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request Request
     * @param \Psr\Http\Message\ResponseInterface $response Response
     * @return \Authentication\AuthenticationServiceInterface
     */
    public function getAuthenticationService(ServerRequestInterface $request) : AuthenticationServiceInterface
    {
        $service = new AuthenticationService();
        // Configure the service. (see below for more details)
        return $service;
    }
}

接下来将 AuthenticationMiddleware 添加到您的应用程序

// in src/Application.php
public function middleware($middlewareQueue)
{
    // Various other middlewares for error handling, routing etc. added here.

    // Add the middleware to the middleware queue
    $middlewareQueue->add(new AuthenticationMiddleware($this));

    return $middlewareQueue;
}

迁移 AuthComponent 设置

来自 AuthComponent 的配置数组需要在配置服务时拆分为标识符和验证器。因此,当您以这种方式配置 AuthComponent

$this->loadComponent('Auth', [
    'authentication' => [
        'Form' => [
            'fields' => [
                'username' => 'email',
                'password' => 'password',
            ]
        ]
    ]
]);

您现在必须以这种方式配置它

// Instantiate the service
$service = new AuthenticationService();

// Load identifiers
$service->loadIdentifier('Authentication.Password', [
    'fields' => [
        'username' => 'email',
        'password' => 'password',
    ]
]);

// Load the authenticators
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form');

如果您已自定义 userModel,您可以使用以下配置

// Instantiate the service
$service = new AuthenticationService();

// Load identifiers
$service->loadIdentifier('Authentication.Password', [
    'resolver' => [
        'className' => 'Authentication.Orm',
        'userModel' => 'Employees',
    ],
    'fields' => [
        'username' => 'email',
        'password' => 'password',
    ]
]);

虽然代码比以前多了一些,但您在处理身份验证的方式上拥有更大的灵活性。

登录操作

AuthenticationMiddleware 将处理根据您的验证器检查和设置身份。通常在登录后,AuthComponent 会重定向到配置的位置。要成功登录后重定向,请更改您的登录操作以检查新的身份结果

public function login()
{
    $result = $this->Authentication->getResult();

    // regardless of POST or GET, redirect if user is logged in
    if ($result->isValid()) {
        $target = $this->Authentication->getLoginRedirect();
        return $this->redirect($target);
    }

    // display error if user submitted and authentication failed
    if ($this->request->is(['post'])) {
        $this->Flash->error('Invalid username or password');
    }
}

检查身份

应用中间件后,您可以使用 identity 请求属性来使用身份数据。这将取代您现在使用的 $this->Auth->user() 调用。如果当前用户未经身份验证或提供的凭据无效,则 identity 属性将为 null

$user = $request->getAttribute('identity');

有关身份验证过程结果的更多详细信息,您可以访问也随请求提供的结果对象,该对象可在 authentication 属性上访问

$result = $request->getAttribute('authentication')->getResult();
// Boolean if the result is valid
$isValid = $result->isValid();
// A status code
$statusCode = $result->getStatus();
// An array of error messages or data if the identifier provided any
$errors = $result->getErrors();

在您调用 AuthComponent::setUser() 的任何地方,您现在都应该使用 setIdentity()

// Assume you need to read a user by access token
$user = $this->Users->find('byToken', ['token' => $token])->first();

// Persist the user into configured authenticators.
$this->Authentication->setIdentity($user);

迁移允许/拒绝逻辑

AuthComponent 一样,AuthenticationComponent 使得使特定操作“公开”变得容易,并且不需要有效的身份存在

// In your controller's beforeFilter method.
$this->Authentication->allowUnauthenticated(['view']);

allowUnauthenticated() 的每次调用都将覆盖当前的操作列表。

要模拟 $this->Auth->deny(['register']);,您可以执行以下操作

$action = $this->getRequest()->getParam('action');
if ($action !== 'register') {
    $this->Authentication->allowUnauthenticated([$action]);
}

迁移未经身份验证的重定向

默认情况下,AuthComponent 会在需要身份验证时将用户重定向回登录页面。相反,此插件中的 AuthenticationComponent 将在这种情况下引发异常。您可以使用 unauthenticatedRedirect 将此异常转换为重定向,在配置 AuthenticationService 时使用。

您还可以使用 queryParam 选项将当前请求目标 URI 作为查询参数传递

// In the getAuthenticationService() method of your src/Application.php

$service = new AuthenticationService();

// Configure unauthenticated redirect
$service->setConfig([
    'unauthenticatedRedirect' => '/users/login',
    'queryParam' => 'redirect',
]);

然后,在控制器登录方法中,您可以使用 getLoginRedirect() 从查询字符串参数中安全地获取重定向目标

public function login()
{
    $result = $this->Authentication->getResult();

    // Regardless of POST or GET, redirect if user is logged in
    if ($result->isValid()) {
        // Use the redirect parameter if present.
        $target = $this->Authentication->getLoginRedirect();
        if (!$target) {
            $target = ['controller' => 'Pages', 'action' => 'display', 'home'];
        }
        return $this->redirect($target);
    }
}

迁移哈希升级逻辑

如果您的应用程序使用 AuthComponent 的哈希升级功能。您可以通过利用 AuthenticationService 来复制该逻辑

public function login()
{
    $result = $this->Authentication->getResult();

    // regardless of POST or GET, redirect if user is logged in
    if ($result->isValid()) {
        $authService = $this->Authentication->getAuthenticationService();

        // Assuming you are using the `Password` identifier.
        if ($authService->identifiers()->get('Password')->needsPasswordRehash()) {
            // Rehash happens on save.
            $user = $this->Users->get($this->Authentication->getIdentityData('id'));
            $user->password = $this->request->getData('password');
            $this->Users->save($user);
        }

        // Redirect to a logged in page
        return $this->redirect([
            'controller' => 'Pages',
            'action' => 'display',
            'home'
        ]);
    }
}