创建自定义的User Provider
Symfony标准认证过程中的一部分是取决于“user providers”。当用户提交用户名和密码时,认证层(authentication layer)会请求配置好的user provider返回给定用户名的user对象。Symfony接下来要检查用户的密码是否正确,并生成一个 security token,以便用户在当前会话期间能够保持认证过的身份。Symfony最牛之处,在于有四个user provider:in_memory
,entity
,ldap
和 chain
。在本节,你将看到如何创建自己的user provider,如果你透过一个自定义的数据库、一个文件、或者 - 如本例所展示的 - 一个web service来访问用户的话,它将非常有用。
创建User类 P
首先,无论你的user数据从 哪里 获取,你都需要创建一个 User
类来呈现那些数据。 User
可以是你希望的任何样子,并且能容纳任何数据。唯一的要求,是这个类必须实现 UserInterface
接口。此接口中的方法应该在自定义的user类中进行定义: getRoles()
,
getPassword()
,
getSalt()
,
getUsername()
,
eraseCredentials()
。去实现 EquatableInterface
接口可能也是有用的,它定义了一个方法,用于检查user是否等同于当前用户。此接口需要一个 isEqualTo()
方法。
这就是你的 WebserviceUser
类实际看起来的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// src/AppBundle/Security/User/WebserviceUser.PHP
namespace AppBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
class WebserviceUser implements UserInterface, EquatableInterface
{
private $username;
private $password;
private $salt;
private $roles;
public function __construct($username, $password, $salt, array $roles)
{
$this->username = $username;
$this->password = $password;
$this->salt = $salt;
$this->roles = $roles;
}
public function getRoles()
{
return $this->roles;
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return $this->salt;
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof WebserviceUser) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->salt !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
} |
如果你有更多关于用户的信息 - 像是一个“first name” - 你可以添加一个 firstName
字段来持有该数据。
创建User Provider P
现在,你有了一个 User
类,你要创建一个user provider,它会从一些web service中获取用户信息,创建一个 WebserviceUser
对象,并对其填充数据。
user provider仅仅是一个实现了UserProviderInterface
接口的原生PHP类,它需要定义三个方法:loadUserByUsername($username)
, refreshUser(UserInterface $user)
和 supportsClass($class)
。更多细节,参考 UserProviderInterface
。
下例是它可能的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// src/AppBundle/Security/User/WebserviceUserProvider.php
namespace AppBundle\Security\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class WebserviceUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
// make a call to your webservice here
// 在此调用你的webservice
$userData = ...
// pretend it returns an array on success, false if there is no user
// 假设在成功时返回的是一个数组,没有用户时则返回false
if ($userData) {
$password = '...';
// ...
return new WebserviceUser($username, $password, $salt, $roles);
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof WebserviceUser) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'AppBundle\Security\User\WebserviceUser';
}
} |
把User Provider设为服务 P
现在把user provider设为服务:
1 2 3 4 |
# app/config/services.yml
services:
app.webservice_user_provider:
class: AppBundle\Security\User\WebserviceUserProvider |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="Http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.webservice_user_provider"
class="AppBundle\Security\User\WebserviceUserProvider"
/>
</services>
</container> |
1 2 3 4 5 6 7 |
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition(
'app.webservice_user_provider',
new Definition('AppBundle\Security\User\WebserviceUserProvider')
); |
user provider的真正实现,可能还需要一些依赖或配置选项,以及其他服务。把所有这些当作参数传入服务定义中。
修改security.yml P
所有内容在你的security配置信息中汇集。把user provider添加到“security”根键下的的providers列表中。并且为这个user provider选择一个名称(如 “webservice”),把你刚才定义的服务标记为 id
键的值。
1 2 3 4 5 6 7 |
# app/config/security.yml
security:
# ...
providers:
webservice:
id: app.webservice_user_provider |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- app/config/security.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<!-- ... -->
<provider name="webservice" id="app.webservice_user_provider" />
</config>
</srv:container> |
1 2 3 4 5 6 7 8 9 10 |
// app/config/security.php
$container->loadFromExtension('security', array(
// ...
'providers' => array(
'webservice' => array(
'id' => 'app.webservice_user_provider',
),
),
)); |
Symfony还必须知道如何去加密网站用户们所提供的密码,如,在登录表单中填写的密码。你可以在security配置中添加一行“encoders”键:
1 2 3 4 5 6 |
# app/config/security.yml
security:
# ...
encoders:
AppBundle\Security\User\WebserviceUser: bcrypt |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- app/config/security.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<!-- ... -->
<encoder class="AppBundle\Security\User\WebserviceUser"
algorithm="bcrypt" />
</config>
</srv:container> |
1 2 3 4 5 6 7 8 9 |
// app/config/security.php
$container->loadFromExtension('security', array(
// ...
'encoders' => array(
'AppBundle\Security\User\WebserviceUser' => 'bcrypt',
),
// ...
)); |
当创建用户时(不管这些用户是如何创建的),不管密码是如何被加密的,此处的键值必须与该加密方式相对应。当用户提交其密码时,密码使用该算法加密,加密结果会和 getPassword()
方法所返回的“密码的hash值” 比对。