现在我们的 CMS 有了用户,我们可以使用 cakephp/authentication 插件来启用他们登录。我们首先要确保密码安全地存储在我们的数据库中。然后我们将提供一个可用的登录和注销功能,并允许新用户注册。
使用 composer 安装身份验证插件
composer require "cakephp/authentication:^3.0"
您需要在数据库中创建 Controller
、Table
、Entity
和模板,用于 users
表。您可以像以前为 ArticlesController 手动执行此操作,也可以使用 bake shell 为您生成类,方法是
bin/cake bake all users
如果您使用此设置创建或更新用户,您可能会注意到密码以纯文本形式存储。从安全角度来看,这非常不好,所以让我们修复它。
这也是谈论 CakePHP 中模型层的好时机。在 CakePHP 中,我们使用不同的类来操作记录集合和单个记录。操作实体集合的方法放在 Table
类中,而属于单个记录的功能放在 Entity
类中。
例如,密码哈希是在单个记录上完成的,因此我们将在实体对象上实现此行为。因为我们希望在每次设置密码时对其进行哈希,所以我们将使用一个变异器/设置器方法。CakePHP 会在您实体中的任何属性被设置时调用一个基于约定的设置器方法。让我们为密码添加一个设置器。在 src/Model/Entity/User.php 中添加以下内容
<?php
namespace App\Model\Entity;
use Authentication\PasswordHasher\DefaultPasswordHasher; // Add this line
use Cake\ORM\Entity;
class User extends Entity
{
// Code from bake.
// Add this method
protected function _setPassword(string $password) : ?string
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher())->hash($password);
}
return null;
}
}
现在,将您的浏览器指向 https://127.0.0.1:8765/users 以查看用户列表。请记住,您需要运行本地服务器。使用 bin/cake server
启动独立 PHP 服务器。
您可以编辑在 安装 期间创建的默认用户。如果您更改该用户的密码,您应该在列表或查看页面上看到哈希后的密码而不是原始值。CakePHP 默认使用 bcrypt 对密码进行哈希。我们建议所有新应用程序使用 bcrypt 以保持较高的安全标准。这是 PHP 推荐的密码哈希算法。
注意
现在为至少一个用户帐户创建一个哈希后的密码!它将在接下来的步骤中需要。更新密码后,您将看到一个长字符串存储在密码列中。请注意,即使对同一个密码保存两次,bcrypt 也会生成不同的哈希值。
现在是配置身份验证插件的时候了。该插件将使用 3 个不同的类来处理身份验证过程
Application
将使用身份验证中间件并提供一个身份验证服务,其中包含我们要定义如何检查凭据以及在何处查找凭据的所有配置。
AuthenticationService
将是一个实用程序类,允许您配置身份验证过程。
AuthenticationMiddleware
将作为中间件队列的一部分执行,这发生在框架处理您的控制器之前,并将提取凭据并处理它们以检查用户是否已通过身份验证。
如果您还记得,我们之前使用 AuthComponent 来处理所有这些步骤。现在逻辑被分成特定的类,并且身份验证过程发生在您的控制器层之前。首先,它会检查用户是否已通过身份验证(根据您提供的配置),并将用户和身份验证结果注入请求中以供进一步引用。
在 src/Application.php 中,添加以下导入
// In src/Application.php add the following imports
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;
然后在您的 Application
类上实现身份验证接口
// in src/Application.php
class Application extends BaseApplication
implements AuthenticationServiceProviderInterface
{
然后添加以下内容
// src/Application.php
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
// ... other middleware added before
->add(new RoutingMiddleware($this))
->add(new BodyParserMiddleware())
// Add the AuthenticationMiddleware. It should be after routing and body parser.
->add(new AuthenticationMiddleware($this));
return $middlewareQueue;
}
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$authenticationService = new AuthenticationService([
'unauthenticatedRedirect' => Router::url('/users/login'),
'queryParam' => 'redirect',
]);
// Load identifiers, ensure we check email and password fields
$authenticationService->loadIdentifier('Authentication.Password', [
'fields' => [
'username' => 'email',
'password' => 'password',
],
]);
// Load the authenticators, you want session first
$authenticationService->loadAuthenticator('Authentication.Session');
// Configure form data check to pick email and password
$authenticationService->loadAuthenticator('Authentication.Form', [
'fields' => [
'username' => 'email',
'password' => 'password',
],
'loginUrl' => Router::url('/users/login'),
]);
return $authenticationService;
}
在您的 AppController
类中,添加以下代码
// src/Controller/AppController.php
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Flash');
// Add this line to check authentication result and lock your site
$this->loadComponent('Authentication.Authentication');
现在,在每个请求中,AuthenticationMiddleware
将检查请求会话以查找已通过身份验证的用户。如果我们正在加载 /users/login
页面,它还会检查已发布的表单数据(如果有)以提取凭据。默认情况下,凭据将从请求数据中的 username
和 password
字段中提取。身份验证结果将被注入名为 authentication
的请求属性中。您可以使用 $this->request->getAttribute('authentication')
从您的控制器操作中随时检查结果。您所有的页面都将受到限制,因为 AuthenticationComponent
正在每个请求上检查结果。当它未能找到任何已通过身份验证的用户时,它会将用户重定向到 /users/login
页面。请注意,此时,该网站将无法正常工作,因为我们还没有登录页面。如果您访问您的网站,您将遇到“无限重定向循环”,所以让我们修复它。
注意
如果您的应用程序在 SSL 和非 SSL 协议中都提供服务,那么当您的应用程序使用非 SSL 协议时,您可能会遇到会话丢失的问题。您需要通过将 config/app.php 或 config/app_local.php 中的 session.cookie_secure 设置为 false 来启用访问。(参见 CakePHP 对 session.cookie_secure 的默认设置)
在您的 UsersController
中,添加以下代码
public function beforeFilter(\Cake\Event\EventInterface $event)
{
parent::beforeFilter($event);
// Configure the login action to not require authentication, preventing
// the infinite redirect loop issue
$this->Authentication->addUnauthenticatedActions(['login']);
}
public function login()
{
$this->request->allowMethod(['get', 'post']);
$result = $this->Authentication->getResult();
// regardless of POST or GET, redirect if user is logged in
if ($result && $result->isValid()) {
// redirect to /articles after login success
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Articles',
'action' => 'index',
]);
return $this->redirect($redirect);
}
// display error if user submitted and authentication failed
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->error(__('Invalid username or password'));
}
}
为您的登录操作添加模板逻辑
<!-- in /templates/Users/login.php -->
<div class="users form">
<?= $this->Flash->render() ?>
<h3>Login</h3>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Please enter your username and password') ?></legend>
<?= $this->Form->control('email', ['required' => true]) ?>
<?= $this->Form->control('password', ['required' => true]) ?>
</fieldset>
<?= $this->Form->submit(__('Login')); ?>
<?= $this->Form->end() ?>
<?= $this->Html->link("Add User", ['action' => 'add']) ?>
</div>
现在登录页面将允许我们正确登录到应用程序。通过请求您网站的任何页面来测试它。在被重定向到 /users/login
页面后,输入您之前创建用户时选择的电子邮件和密码。登录成功后,您应该被成功重定向。
我们需要添加一些额外的细节来配置我们的应用程序。我们希望所有 view
和 index
页面都可以在不登录的情况下访问,因此我们将此特定配置添加到 AppController 中
// in src/Controller/AppController.php
public function beforeFilter(\Cake\Event\EventInterface $event)
{
parent::beforeFilter($event);
// for all controllers in our application, make index and view
// actions public, skipping the authentication check
$this->Authentication->addUnauthenticatedActions(['index', 'view']);
}
注意
如果您还没有使用哈希后的密码的用户,请在您的 AppController 中注释掉 $this->loadComponent('Authentication.Authentication')
行,以及所有其他使用身份验证的行。然后转到 /users/add
以创建一个新用户,选择电子邮件和密码。之后,确保取消注释我们刚刚暂时注释掉的行!
在登录之前访问 /articles/add
来试一试!由于此操作不允许,您将被重定向到登录页面。成功登录后,CakePHP 将自动将您重定向回 /articles/add
。
将注销操作添加到 UsersController
类中
// in src/Controller/UsersController.php
public function logout()
{
$result = $this->Authentication->getResult();
// regardless of POST or GET, redirect if user is logged in
if ($result && $result->isValid()) {
$this->Authentication->logout();
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}
}
现在您可以访问 /users/logout
以注销。然后您应该被发送到登录页面。
如果您尝试在未登录的情况下访问 /users/add,您将被重定向到登录页面。我们应该修复这个问题,因为我们希望允许人们注册我们的应用程序。在 UsersController
中修复以下行
// Add to the beforeFilter method of UsersController
$this->Authentication->addUnauthenticatedActions(['login', 'add']);
上面的代码告诉 AuthenticationComponent
,UsersController
的 add()
操作不需要身份验证或授权。您可能需要花时间整理 Users/add.php 并删除误导性的链接,或者继续下一部分。我们不会在此教程中构建用户编辑、查看或列出功能,但这项练习您可以自行完成。
现在用户可以登录了,我们希望通过 应用授权策略 来限制用户只能编辑他们创建的文章。