我(再次)遇到了 Symfony2 的安全组件问题。
首先,身份验证过程涉及多个对象:
- 安全工厂接口
- AbstractAuthenticationListener
- 身份验证提供者接口
- 自定义令牌(从 AbstractToken 继承)
- 用户提供者接口
流动
如果我理解得很好,安全工厂的目的是配置自定义身份验证。
侦听器是身份验证的指挥者。通过它的attemptAuthentication
方法,它捕获表单提交(包含用户的凭据)并尝试对用户进行身份验证。在此方法中,Listener 将创建一个AbstractToken
然后将令牌传递authenticate
给AuthenticationProvider
.
之后,AuthenticationProvider
调用UserProvider
从网络服务或数据库等检索用户数据......
一旦UserProvider
它发挥了魔力,它就会返回一个 User 对象给AuthenticationProvider
.
AuthenticationProvider 然后创建一个新的 Token 填充由 检索到的用户UserProvider
,然后将其返回到Listener
.
获得新令牌后,它Listener
会做一些未知的魔法(我认为它将令牌设置为安全上下文,但我不确定)。
问题
在每个步骤中,我都对我的用户对象进行了 var_dump。除了“最后的步骤”之外,每个步骤都设置了角色:在Listener
.
当从 中Listener
检索经过身份验证的令牌时UserProvider
,用户的角色为空。我想不通为什么...
安全工厂
class CompanyFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.company.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('company.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider));
$listenerId = 'security.authentication.listener.company.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('company.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'company';
}
public function addConfiguration(NodeDefinition $node)
{
}
}
听众
class CompanyListener extends AbstractAuthenticationListener
{
// Custructor stuff removed
protected function requiresAuthentication(Request $request)
{
if ($this->options['post_only'] && !$request->isMethod('post'))
{
return false;
}
return parent::requiresAuthentication($request);
}
protected function attemptAuthentication(Request $request)
{
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
$ip = $request->getClientIp();
$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
$authToken = $this->authenticationManager->authenticate(new CompanyUserToken($username, $password, $ip));
return $authToken;
}
}
身份验证提供者
class CompanyProvider implements AuthenticationProviderInterface
{
private $userProvider;
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}
public function authenticate(TokenInterface $token)
{
$user = $this->userProvider->loadUserByUsernamePassword($token->user, $token->getPassword(), $token->getIp());
$authenticatedToken = new CompanyUserToken($user->getUsername(), $user->getPassword(), $user->getIp(), $user->getRoles());
$authenticatedToken->setUser($user);
return $authenticatedToken;
}
public function supports(TokenInterface $token)
{
return $token instanceof CompanyUserToken;
}
}
自定义令牌
class CompanyUserToken extends AbstractToken
{
private $password;
private $ip;
public function __construct($username, $password, $ip, array $roles = array())
{
parent::__construct($roles);
$this->password = $password;
$this->user = $username;
$this->ip = $ip;
// If the user has roles, consider it authenticated
$this->setAuthenticated(count($roles) > 0);
}
public function getCredentials()
{
return '';
}
public function getPassword()
{
return $this->password;
}
public function getIp()
{
return $this->ip;
}
}
用户提供者
class CompanyUserProvider implements UserProviderInterface
{
private $documentManager;
public function __construct($doctrineMongoDB)
{
$this->doctrineMongoDB = $doctrineMongoDB;
}
public function loadUserByUsername($username)
{
// Not used but needed by the interface
}
public function loadUserByUsernamePassword($username, $password, $ip)
{
// Does the magic, retrieve user datas from DB.
return $user;
}
public function refreshUser(UserInterface $user)
{
// Does nearly the same thing that the above method
return $refreshedUser;
}
public function supportsClass($class)
{
return $class === 'Company\UserBundle\Document\User';
}
}
服务.yml
parameters:
security.authentication.handler.class: Company\UserBundle\Security\Authentication\Handler\CompnayAuthenticationHandler
company_user_provider.class: Company\UserBundle\Security\User\CompanyUserProvider
services:
security.authentication.handler:
class: %security.authentication.handler.class%
public: false
arguments: [@router, @security.http_utils]
company.security.authentication.provider:
class: Company\UserBundle\Security\Authentication\Provider\CompanyProvider
arguments: [@company_user_provider]
company.security.authentication.listener:
class: Company\UserBundle\Security\Firewall\CompanyListener
arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils, "company", @security.authentication.handler, @security.authentication.handler, {}, @logger, @event_dispatcher, @user.service.captcha]
company_user_provider:
class: %company_user_provider.class%
arguments: [@doctrine_mongodb]
user.service.captcha:
class: Company\UserBundle\Services\CaptchaService
arguments: [@form.factory]
安全.yml
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
Company\UserBundle\Document\User: plaintext
role_hierarchy:
ROLE_VIP_USER: ROLE_USER
ROLE_ADMIN: [ROLE_USER, ROLE_VIP_USER]
providers:
webservice:
id: company_user_provider
firewalls:
company_secured:
pattern: ^/
company: true
anonymous: true
form_login:
login_path: login
check_path: login_check
post_only: true
use_referer: false
success_handler: security.authentication.handler
failure_handler: security.authentication.handler
logout:
path: /logout
target: login
access_control:
- { path: ^/admin, role: ROLE_ADMIN }
更新
这里有一些var_dumps
解释我的问题:
用户提供者
var_dump($user);
object(Company\UserBundle\Document\User)[142]
protected 'username' => string 'Supacoco' (length=13)
protected 'roles' =>
array (size=1)
0 => string 'ROLE_ADMIN' (length=10)
身份验证提供者
var_dump($authenticatedToken->getUser());
object(Company\UserBundle\Document\User)[142]
protected 'username' => string 'Supacoco' (length=13)
protected 'roles' =>
array (size=1)
0 => string 'ROLE_ADMIN' (length=10)
听众
var_dump($authToken->getUser());
object(Company\UserBundle\Document\User)[142]
protected 'username' => string 'Supacoco' (length=13)
protected 'roles' =>
array (size=0)
empty