安全性:密码检查删除和自定义用户提供程序 [英] Security: password check removal and custom User Provider
问题描述
我正在创建 Symfony2(版本 2.0.16)custom User Provider
以使用我们的 LDAP 服务器,但根据 如何创建自定义用户提供程序 文档,密码检查在 Symfony2 端完成:
I'm creating Symfony2 (Version 2.0.16) custom User Provider
to work with our LDAP Server, but according How to create a custom User Provider document, password checking is done on Symfony2 side:
当用户提交用户名和密码时,认证层要求配置的用户提供者返回给定的用户对象用户名.Symfony 然后检查这个用户的密码是否是正确并生成安全令牌,以便用户保持身份验证在当前会话期间.
When a user submits a username and password, the authentication layer asks the configured user provider to return a user object for a given username. Symfony then checks whether the password of this user is correct and generates a security token so the user stays authenticated during the current session.
首先,我不喜欢将用户密码传回 Symfony 的想法.其次,我们已经有了 LDAP Web 服务,它会检查密码是否匹配并更改它会出现问题.
First of all I don't like the idea of passing User passwords back to Symfony. Secondly, we already have LDAP Web Service which checks if password matched on its side and changing it would be problematic.
问题:如何从 Symfony 中删除密码检查并让它依赖 LDAP Web返回布尔 IsAuth
标志的服务?
Question: How can remove password checking from Symfony and let it rely on LDAP Web
Service which returns Boolean IsAuth
flag?
这就是我现在查询 LDAP Web 服务的方式:
This how I query LDAP Web Service now:
// The output has IsAuth flag
$this->get('LDAP_user_provider')
->searchMember($request->get('username'), $request->get('password'));
推荐答案
好吧,这不是微不足道的,但我会尽量给你提供尽可能多的信息.你需要为 Symfony 2.0 做一些小的改变,我的解决方案是 2.1.我希望没有复制/粘贴问题,没有错别字或缺少配置.首先,您需要创建一个 AuthenticationProvider
,例如:
Ok, this is not super trivial, but I'll try to give you as much information as possible. There are some small changes you need to do for Symfony 2.0, my solution is for 2.1. I hope there are no copy/paste problems, and no typos or missing configuration. First, you will want to create a AuthenticationProvider
, something like:
<?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):
Register the service (with 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 中注册:
and register it in you 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
}
}
然后您需要在侦听器中创建该令牌,例如:
Then you need to create that Token in a listener, something like:
<?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):
and register that as a service as well (using 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"
希望能帮助您入门!
这篇关于安全性:密码检查删除和自定义用户提供程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!