我正在创建Symfony2(版本2.0.16)custom User Provider
以使用我们的LDAP服务器,但根据How to create a custom User Provider文档,密码检查在Symfony2端完成:
当用户提交用户名和密码时,身份验证层 要求配置的用户提供程序返回给定的用户对象 用户名。然后Symfony检查该用户的密码是否为 更正并生成安全令牌,以便用户保持身份验证 在本届会议期间。
首先,我不喜欢将用户密码传回Symfony的想法。其次,我们已经拥有LDAP Web服务,它可以检查密码是否匹配,并且更改密码会有问题。
问题:如何从Symfony中删除密码检查并让它依赖于LDAP Web
返回布尔IsAuth
标志的服务?
我现在如何查询LDAP Web服务:
// The output has IsAuth flag
$this->get('LDAP_user_provider')
->searchMember($request->get('username'), $request->get('password'));
答案 0 :(得分:7)
好的,这不是超级微不足道的,但我会尽量给你尽可能多的信息。你需要为Symfony 2.0做一些小改动,我的解决方案是2.1。我希望没有复制/粘贴问题,没有拼写错误或丢失配置。首先,您需要创建AuthenticationProvider
,例如:
<?php
namespace Acme\DemoBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Beryllium\CacheBundle\Cache;
use Acme\DemoBundle\Security\Authentication\Token\RestToken;
/**
* The Provider is the component of the authentication system that authenticates tokens.
*/
class LdapProvider implements AuthenticationProviderInterface
{
private $userProvider;
private $encoderFactory;
/**
* Constructor
* @param UserProviderInterface $userProvider
* @param String $cacheDir
* @param EncoderFactory $encoderFactory
*/
public function __construct(UserProviderInterface $userProvider, EncoderFactory $encoderFactory)
{
$this->userProvider = $userProvider;
$this->encoderFactory = $encoderFactory; // usually this is responsible for validating passwords
}
/**
* This function authenticates a passed in token.
* @param TokenInterface $token
* @return TokenInterface
* @throws AuthenticationException if wrong password or no username
*/
public function authenticate(TokenInterface $token)
{
if (!empty($token->username)) {
$user = $this->userProvider->loadUserByUsername($token->username);
$encoder = $this->encoderFactory->getEncoder($user);
if ($token->needsAuthentication && !$token->isLdapAuthenticated()) {
throw new AuthenticationException('Password wrong');
}
} else {
throw new AuthenticationException('No user');
}
$token->setUser($user);
$token->setAuthenticated(true);
return $token;
}
/**
* @inheritdoc
* @param TokenInterface $token
* @return Boolean
*/
public function supports(TokenInterface $token)
{
return $token instanceof RestToken;
}
}
注册服务(使用XML):
<service id="ldap.security.authentication.provider"
class="Acme\DemoBundle\Security\Authentication\Provider\LdapProvider" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="security.encoder_factory"/>
</service>
或者与YAML:
ldap.security.authentication.provider:
class: Acme\DemoBundle\Security\Authentication\Provider\LdapProvider
public: false
arguments:
- ~
- "@security.encoder_factory"
创建安全工厂:
<?php
namespace Acme\DemoBundle\Security\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
class LdapFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.ldap.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('ldap.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.ldap.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('ldap.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'ldap';
}
public function addConfiguration(NodeDefinition $node)
{}
}
并在你的Bundle中注册:
<?php
namespace Acme\DemoBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Acme\DemoBundle\Security\Factory\LdapFactory;
class AcmeDemoBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new LdapFactory());
}
}
并创建自己的令牌:
namespace Acme\DemoBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
/**
* This is a class that represents a security token that is used for logged in users.
*/
class LdapToken extends AbstractToken
{
public $sessionId;
public $username;
public $password;
public $member;
public $needsAuthentication = true;
public function __construct(array $roles = array())
{
parent::__construct($roles);
}
public function getCredentials()
{
return '';
}
public function getRoles()
{
if ($this->getUser()) {
return $this->getUser()->getRoles();
} else {
return array();
}
}
public function isLdapAuthenticated()
{
return true; // Left as an exercise
}
}
然后你需要在监听器中创建该标记,例如:
<?php
namespace Acme\ApiBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Acme\DemoBundle\Security\Authentication\Token\LdapToken;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
/**
* Class that will listen for log ins and then authorize the user.
*/
class LdapListener implements ListenerInterface
{
/**
* A security context
* @var SecurityContextInterface
*/
protected $securityContext;
/**
* A authentication manager that we will be able to authenticate against
* @var AuthenticationManagerInterface
*/
protected $authenticationManager;
/**
* Constructor
*
* @param SecurityContextInterface $securityContext
* @param AuthenticationManagerInterface $authenticationManager
*/
public function __construct(SecurityContextInterface $securityContext,
AuthenticationManagerInterface $authenticationManager
) {
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
}
/**
* This function is handling the authentication part.
*
* @param GetResponseEvent $event
* @return
*/
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
$token = new LdapToken();
// now populate it with whatever information you need, username, password...
try {
$returnValue = $this->authenticationManager->authenticate($token);
if ($returnValue instanceof TokenInterface) {
if ($token->needsAuthentication) {
if ($event->hasResponse()) {
$response = $event->getResponse();
} else {
$response = new Response();
$event->setResponse($response);
}
}
return $this->securityContext->setToken($returnValue);
} elseif ($returnValue instanceof Response) {
return $event->setResponse($response);
}
} catch (AuthenticationException $e) {
// Do nothing in this case. We are returning a 401 below
}
$response = new Response('UNAUTHORIZED');
$response->setStatusCode(HTTPCodes::HTTP_UNAUTHORIZED);
$event->setResponse($response);
}
}
并将其注册为服务(使用XML):
<service id="ldap.security.authentication.listener"
class="Acme\DemoBundle\Security\Firewall\RestListener" public="false">
<argument type="service" id="security.context"/>
<argument type="service" id="security.authentication.manager" />
</service>
或YAML:
ldap.security.authentication.listener:
class: Acme\DemoBundle\Security\Firewall\RestListener
public: false
arguments:
- "@security.context"
- "@security.authentication.manager"
希望能让你开始!