安全性:密码检查删除和自定义用户提供程序 [英] Security: password check removal and custom User Provider

查看:25
本文介绍了安全性:密码检查删除和自定义用户提供程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆