markdown Zend Framework 3:身份验证
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown Zend Framework 3:身份验证相关的知识,希望对你有一定的参考价值。
# Authentication Service
We use Authentication Service of Zend for implement the authentication feature.
First create your customer Adapter for Authentication Service that name `AuthAdapter`.
## AuthAdapter
```php
namespace User\Service;
use Doctrine\ORM\EntityManager;
use User\Entity\User;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Authentication\Result;
use Zend\Crypt\Password\Bcrypt;
class AuthAdapter implements AdapterInterface {
/**
* User email
* @var string
*/
private $email;
/**
* Password
* @var string
*/
private $password;
/**
* Entity Manager.
* @var EntityManager
*/
private $entityManager;
/**
* AuthAdapter constructor.
* @param $entityManager
*/
public function __construct($entityManager) {
$this->entityManager = $entityManager;
}
/**
* Set user email
* @param $email
*/
public function setEmail($email) {
$this->email = $email;
}
/**
* Set password
* @param $password
*/
public function setPassword($password) {
$this->password = $password;
}
public function authenticate()
{
// Check the database if there is a user with such email.
$user = $this->entityManager->getRepository(User::class)->findOneByEmail($this->email);
// If there is no such user, return 'Identity Not Found' status.
if ($user == null)
return new Result(
Result::FAILURE_IDENTITY_NOT_FOUND,
null,
['Invalid credentials.']
);
// If the user with such email exists, we need to check if it is active or retired.
// Do not allow retired users to log in.
if ($user->getStatus() == User::STATUS_RETIRED)
return new Result(
Result::FAILURE,
null,
['User is retired.']
);
// Now we need to calculate hash based on user-entered password and compare
// it with the password hash stored in database.
$bcrypt = new Bcrypt();
$passwordHash = $user->getPassword();
if ($bcrypt->verify($this->password, $passwordHash))
// Great! The password hash matches. Return user identity (email) to be
// saved in session for later use.
return new Result(
Result::SUCCESS,
$this->email,
['Authenticated successfully.']
);
// If password check didn't pass return 'Invalid Credential' failure status.
return new Result(
Result::FAILURE_CREDENTIAL_INVALID,
null,
['Invalid credentials.']
);
}
}
```
## AuthAdapterFactory
You have to factory `AuthAdapter` too:
```php
namespace User\Service\Factory;
use Interop\Container\ContainerInterface;
use User\Service\AuthAdapter;
use Zend\ServiceManager\Factory\FactoryInterface;
/**
* This is the factory class for AuthAdapter service. The purpose of the factory
* is to instantiate the service and pass it dependencies (inject dependencies)
* @package User\Service\Factory
*/
class AuthAdapterFactory implements FactoryInterface {
/**
* This method creates the AuthAdapter service and returns its instance.
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
* @return object
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// Get Doctrine entity manager from Service Manager.
$entityManager = $container->get('doctrine.entitymanager.orm_default');
// Create the AuthAdapter and inject dependency to its constructor.
return new AuthAdapter($entityManager);
}
}
```
## AuthenticationServiceFactory
You have to inject your `AuthAdapter` to `AuthenticationService`. `AuthenticationServiceFactory` file contains:
```php
namespace User\Service\Factory;
use Interop\Container\ContainerInterface;
use User\Service\AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage\Session;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Session\SessionManager;
/**
* The factory responsible for creating of authentication service.
* Class AuthenticationServiceFactory
* @package User\Service\Factory
*/
class AuthenticationServiceFactory implements FactoryInterface {
/**
* This method creates the Zend\Authentication\AuthenticationService service
* and return its instance.
*
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
* @return object|AuthenticationService
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$sessionManager = $container->get(SessionManager::class);
$authStorage = new Session('Zend_Auth', 'session', $sessionManager);
$authAdapter = $container->get(AuthAdapter::class);
// Create the service and inject dependencies into its constructor.
return new AuthenticationService($authStorage, $authAdapter);
}
}
```
## AuthManager
This file is used to handle login and logout of authentication feature.
```php
namespace User\Service;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Result;
use Zend\Session\SessionManager;
/**
* Class AuthManager
* @package User\Service
*/
class AuthManager {
// Constants returned by the access filter.
const ACCESS_GRANTED = 1; // Access to the page is granted.
const AUTH_REQUIRED = 2; // Authentication is required to see the page.
const ACCESS_DENIED = 3; // Access to the page is denied.
/**
* @var SessionManager
*/
protected $sessionManager;
/**
* @var AuthenticationService
*/
protected $authService;
/**
* Contents of the 'access_filter' config key.
* @var array
*/
protected $config;
/**
* AuthManager constructor.
* @param SessionManager $sessionManager
* @param AuthenticationService $authService
* @param array $config
*/
public function __construct(SessionManager $sessionManager, AuthenticationService $authService, $config)
{
$this->sessionManager = $sessionManager;
$this->authService = $authService;
$this->config = $config;
}
/**
* Performs a login attempt. If $rememberMe argument is true, if forces the session
* to last for one month (otherwise the session expires on one hour).
*
* @param $email
* @param $password
* @param $rememberMe
* @return Result
* @throws \Exception
*/
public function login($email, $password, $rememberMe) {
if ($this->authService->getIdentity() != null)
throw new \Exception('Already logged in');
$authAdapter = $this->authService->getAdapter();
$authAdapter->setEmail($email);
$authAdapter->setPassword($password);
$result = $this->authService->authenticate();
// If user wants to "remember him", we will make session to expire in
// one month. By default session expires in 1 hour (as specific in our
// config/global.php file).
if ($result->getCode() == Result::SUCCESS && $rememberMe)
// Session cookie will expire in 1 month (30 days).
$this->sessionManager->rememberMe(60*60*24*30);
return $result;
}
/**
* Performs user logout.
* @throws \Exception
*/
public function logout() {
// Allow to log out only when user is logged in.
if ($this->authService->getIdentity() == null)
throw new \Exception('The user is not logged in');
// Remove identity form session.
$this->authService->clearIdentity();
}
/**
* This is a simple access control filter. It is able to restrict unauthorized
* users to visit certain pages.
*
* This method uses the 'access_filter' key in the config file and determines
* whether the current visitor is allowed to access the given controller action
* or not. It returns true if allowed; otherwise false.
* @param $controllerName
* @param $actionName
* @return bool
* @throws \Exception
*/
public function filterAccess($controllerName, $actionName) {
// Determine mode - 'restrictive' (default) or 'permissive'. In restrictive
// mode all controller actions must be explicitly listed under the 'access_filter'
// config key, and access is denied to any not listed action for unauthorized users.
// In permissive mode, if an action is not listed under the 'access_filter' key,
// access to it is permitted to anyone (even for not logged in users.
// Restrictive mode is more secure and recommended to use.
$mode = isset($this->config['options']['mode']) ? $this->config['options']['mode'] : 'restrictive';
if ($mode != 'restrictive' && $mode != 'permissive')
throw new \Exception('Invalid filter access mode (expected either restrictive or permissive mode)');
if (isset($this->config['controllers'][$controllerName])) {
$items = $this->config['controllers'][$controllerName];
foreach ($items as $item) {
$actionList = $item['actions'];
$allow = $item['allow'];
if (is_array($actionList) && in_array($actionName, $actionList) ||
$actionList == '*') {
if ($allow == '*')
return true; // Anyone is allowed to see the page.
elseif ($allow == '@' && $this->authService->hasIdentity()) {
return true; // Only authenticated user is allowed to see the page.
} else {
return false; // Access denied.
}
}
}
}
// In restrictive mode, we forbid access for authenticated users to any
// action not listed under 'access_filter' key (for security reasons).
if ($mode == 'restrictive' && !$this->authService->hasIdentity())
return false;
// Permit access to this page.
return true;
}
}
```
## AuthManagerFactory
```php
namespace User\Service\Factory;
use Interop\Container\ContainerInterface;
use User\Service\AuthManager;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Session\SessionManager;
class AuthManagerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// Instantiate dependencies.
$sessionManager = $container->get(SessionManager::class);
$authenticationService = $container->get(AuthenticationService::class);
// Get contents of 'access_filter' config key (the AuthManager service
// will use this data to determine whether to allow currently logged in user
// to execute the controller action or not.
$config = $container->get('Config');
if (isset($config['access_filter']))
$config = $config['access_filter'];
else
$config = [];
// Instantiate the AuthManager service and inject dependencies to its constructor.
return new AuthManager($sessionManager, $authenticationService, $config);
}
}
```
## AuthController
This file is used to handle login/logout page by using `AuthManager`.
```php
namespace User\Controller;
use Doctrine\ORM\EntityManager;
use User\Form\LoginForm;
use User\Service\AuthManager;
use User\Service\UserManager;
use Zend\Authentication\Result;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Uri\Uri;
use Zend\View\Model\ViewModel;
/**
* This controller is responsible for letting the user to log in and log out.
*
* Class AuthController
* @package User\Controller
*/
class AuthController extends AbstractActionController {
/**
* EntityManager
* @var EntityManager
*/
private $entityManager;
/**
* Auth Manager.
* @var AuthManager
*/
private $authManager;
/**
* User manager.
* @var UserManager
*/
private $userManager;
/**
* AuthController constructor.
* @param $entityManager
* @param $authManager
* @param $userManager
*/
public function __construct(
$entityManager,
$authManager,
$userManager
) {
$this->entityManager = $entityManager;
$this->authManager = $authManager;
$this->userManager = $userManager;
}
/**
* @return ViewModel
* @throws \Exception
*/
public function loginAction() {
// Retrieve the redirect URL (if passed). We will redirect the user to this
// URL after successful login.
$redirectUrl = (string) $this->params()->fromQuery('redirectUrl', '');
if (strlen($redirectUrl) > 2048)
throw new \Exception("Too long redirectUrl argument passed.");
// Check if we do not have users in database at all. If so, create
// the 'Admin' user.
$this->userManager->createAdminUserIfNotExists();
// Create login form
$form = new LoginForm();
$form->get('redirect_url')->setValue($redirectUrl);
// Store login status
$isLoginError = false;
// Check if user has submitted the form
if ($this->getRequest()->isPost()) {
// Fill in the form with POST data
$data = $this->params()->fromPost();
$form->setData($data);
// Validate form
if ($form->isValid()) {
// Get filtered and validated data
$data = $form->getData();
// Perform login attempt.
$result = $this->authManager->login($data['email'], $data['password'], $data['remember_me']);
// Check result
if ($result->getCode() == Result::SUCCESS) {
// Get redirect URL.
$redirectUrl = $this->params()->fromPost('redirect_url', '');
if (!empty($redirectUrl)) {
// The below check is to prevent possible redirect attack
// (if someone tries to redirect user to another domain).
$uri = new Uri($redirectUrl);
if (!$uri->isValid() || $uri->getHost() != null)
throw new \Exception('Incorrect redirect URL: ' . $redirectUrl);
}
// If redirect URL is provided, redirect the user to that URL;
// otherwise redirect to Home page.
if (empty($redirectUrl)) {
return $this->redirect()->toRoute('home');
} else {
$this->redirect()->toUrl($redirectUrl);
}
} else {
$isLoginError = true;
}
} else {
$isLoginError = true;
}
}
return new ViewModel([
'form' => $form,
'isLoginError' => $isLoginError,
'redirectUrl' => $redirectUrl
]);
}
/**
* The "logout" action performs logout operation.
*
* @return \Zend\Http\Response
* @throws \Exception
*/
public function logoutAction() {
$this->authManager->logout();
return $this->redirect()->toRoute('login');
}
/**
* Displays the "Not Authorized" page.
* @return ViewModel
*/
public function notAuthorizedAction() {
$this->getResponse()->setStatusCode(403);
return new ViewModel();
}
}
```
## AuthControllerFactory
Use for init the `AuthController`.
```php
namespace User\Controller\Factory;
use Interop\Container\ContainerInterface;
use User\Controller\AuthController;
use User\Service\AuthManager;
use User\Service\UserManager;
use Zend\ServiceManager\Factory\FactoryInterface;
/**
* This is the factory for AuthController. Its purpose is to instantiate the controller
* and inject dependencies into its constructor.
* @package User\Controller\Factory
*/
class AuthControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$authManager = $container->get(AuthManager::class);
$userManager = $container->get(UserManager::class);
return new AuthController($entityManager, $authManager, $userManager);
}
}
```
# Extendable Capability
You can custom `Adapter`, `Storage` with interface `Zend\Authentication\Adapter` and `Zend\Authentication\Storage\StorageInterface`
# Supported Adapter
- `Zend\Authentication\Adapter\Callback`
- `Zend\Authentication\Adapter\Http`
- `Zend\Authentication\Adapter\Digest`
- `Zend\Authentication\Adapter\Ldap`
- `Zend\Authentication\Adapter\DbTable`
# Support Storage
- `Zend\Authentication\Storage\Session`
- `Zend\Authentication\Storage\Chain`
- `Zend\Authentication\Storage\NonPersistent`
以上是关于markdown Zend Framework 3:身份验证的主要内容,如果未能解决你的问题,请参考以下文章
markdown Zend Framework 3:授权和RBAC
markdown Zend Framework 3:Controller插件管理器
markdown Zend Framework 3:RBAC的访问过滤器和访问视图助手
markdown Zend Framework 3:用于身份验证服务的CurrentUser Filter和CurrentUser View Helper