WSSE的身份验证提供程序

时间:2015-06-03 09:30:36

标签: php mysql symfony doctrine-orm

我尝试按照symfony cookbook Symfony Link提供的步骤创建一个使用wsse的身份验证提供程序。但是我遇到了一个我一直试图解决的意外错误(已经过了3天)。

这是我的错误:

  

ArrayNode.php第309行中的InvalidConfigurationException:   无法识别的选项" wsse"在" security.firewalls.wsse_secured"

那是我的WsseProvider

namespace OBCarsTest2Bundle\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\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use OBCarsTest2Bundle\Security\Authentication\Token\WsseUserToken;
use Symfony\Component\Security\Core\Util\StringUtils;

class WsseProvider implements AuthenticationProviderInterface
{
    private $userProvider;
    private $cacheDir;

    public function __construct(UserProviderInterface $userProvider, $cacheDir)
    {
        $this->userProvider = $userProvider;
        $this->cacheDir     = $cacheDir;
    }

    public function authenticate(TokenInterface $token)
    {
        $user = $this->userProvider->loadUserByUsername($token->getUsername());

        if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
            $authenticatedToken = new WsseUserToken($user->getRoles());
            $authenticatedToken->setUser($user);

            return $authenticatedToken;
        }

        throw new AuthenticationException('The WSSE authentication failed.');
    }

    /**
     * This function is specific to Wsse authentication and is only used to help this example
     *
     * For more information specific to the logic here, see
     * https://github.com/symfony/symfony-docs/pull/3134#issuecomment-27699129
     */
    protected function validateDigest($digest, $nonce, $created, $secret)
    {
        // Check created time is not in the future
        if (strtotime($created) > time()) {
            return false;
        }

        // Expire timestamp after 5 minutes
        if (time() - strtotime($created) > 300) {
            return false;
        }

        // Validate that the nonce is *not* used in the last 5 minutes
        // if it has, this could be a replay attack
        if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) {
            throw new NonceExpiredException('Previously used nonce detected');
        }
        // If cache directory does not exist we create it
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0777, true);
        }
        file_put_contents($this->cacheDir.'/'.$nonce, time());

        // Validate Secret
        $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));

        return StringUtils::equals($expected, $digest);
    }

    public function supports(TokenInterface $token)
    {
        return $token instanceof WsseUserToken;
    }
}

我的WsseUserToken:

namespace OBCarsTest2Bundle\Security\Authentication\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class WsseUserToken extends AbstractToken
{
    public $created;
    public $digest;
    public $nonce;

    public function __construct(array $roles = array())
    {
        parent::__construct($roles);

        // If the user has roles, consider it authenticated
        $this->setAuthenticated(count($roles) > 0);
    }

    public function getCredentials()
    {
        return '';
    }
}

我的WsseListener:

namespace OBCarsTest2Bundle\Security\Firewall;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use OBCarsTest2Bundle\Security\Authentication\Token\WsseUserToken;

class WsseListener implements ListenerInterface
{
    protected $tokenStorage;
    protected $authenticationManager;

    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
    {
        $this->tokenStorage = $tokenStorage;
        $this->authenticationManager = $authenticationManager;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
        if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
            return;
        }

        $token = new WsseUserToken();
        $token->setUser($matches[1]);

        $token->digest   = $matches[2];
        $token->nonce    = $matches[3];
        $token->created  = $matches[4];

        try {
            $authToken = $this->authenticationManager->authenticate($token);
            $this->tokenStorage->setToken($authToken);

            return;
        } catch (AuthenticationException $failed) {
            // ... you might log something here

            // To deny the authentication clear the token. This will redirect to the login page.
            // Make sure to only clear your token, not those of other authentication listeners.
            $token = $this->tokenStorage->getToken();
            if ($token instanceof WsseUserToken && $this->providerKey === $token->getProviderKey()) {
                $this->tokenStorage->setToken(null);
            }
            return;
        }

        // By default deny authorization
        $response = new Response();
        $response->setStatusCode(Response::HTTP_FORBIDDEN);
        $event->setResponse($response);
    }
}

我的WsseFactory:

namespace OBCarsTest2Bundle\DependencyInjection\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 WsseFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.wsse.'.$id;
        $container
            ->setDefinition($providerId,
              new DefinitionDecorator('wsse.security.authentication.provider'))
            ->replaceArgument(0, new Reference($userProvider))
            ->replaceArgument(2, $config['lifetime']);

        $listenerId = 'security.authentication.listener.wsse.'.$id;
        $listener = $container->setDefinition($listenerId, new DefinitionDecorator('wsse.security.authentication.listener'));

        return array($providerId, $listenerId, $defaultEntryPoint);
    }

    public function getPosition()
    {
        return 'pre_auth';
    }

    public function getKey()
    {
        return 'wsse';
    }

    public function addConfiguration(NodeDefinition $node)
    {
      $node
        ->children()
        ->scalarNode('lifetime')->defaultValue(300)
        ->end();
    }
}

那是我的security.yml:

# you can read more about security in the related section of the documentation
# http://symfony.com/doc/current/book/security.html
security:
    # http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
        # OBCarsTest2Bundle\Entity\user:
            # algorithm: bcrypt
            # cost:      12

    # http://symfony.com/doc/current/book/security.html#hierarchical-roles
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
        # api_key_user_provider:
            # id: api_key_user_provider
        # in_memory:
            # memory:
                # users:
                    # ryan:
                        # password: ryanpass
                        # roles: 'ROLE_USER'
                    # admin:
                        # password: kitten
                        # roles: 'ROLE_ADMIN'
        # our_db_provider:
            # entity:
                # class: OBCarsTest2Bundle\Entity\user
                # property: username
                # if you're using multiple entity managers
                #manager_name: customer

    # the main part of the security, where you can set up firewalls
    # for specific sections of your app
    firewalls:
        wsse_secured:
            pattern:   /api/.*
            stateless: true
            wsse:      { lifetime: 30 }
            anonymous: false
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
        # the login page has to be accessible for everybody
        demo_login:
            pattern:  ^/demo/secured/login$
            security: false

        demo_secured_area:
            pattern:    ^/demo/secured/
            # it's important to notice that in this case _demo_security_check and _demo_login
            # are route names and that they are specified in the AcmeDemoBundle
            form_login:
                check_path: _demo_security_check
                login_path: _demo_login
            logout:
                path:   _demo_logout
                target: _demo

        default:
            #pattern:    ^/
            http_basic: ~
            #provider: our_db_provider
            anonymous: ~

    # with these settings you can restrict or allow access for different parts
    # of your application based on roles, ip, host or methods
    # http://symfony.com/doc/current/book/security.html#security-book-access-control-matching-options
    access_control:
        #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
        - { path: ^/admin, roles: ROLE_ADMIN }

2 个答案:

答案 0 :(得分:0)

您需要导入wsse services.yml或直接在config.yml

中添加它
- (int)getGlobalRowFor:(int)row at:(int)section {
    if(section == 0) {
        return row;
    } else {
        int count = 0;
        for(int i = 0; i < section; i++) {
            // This is where you put your own numbers for section counts. 
            // This is my model, yours could be different.
            count += [[[self.sections objectAtIndex:i] objectForKey:@"sectionCount"] intValue];
        }
        return count + row;
    }
}

答案 1 :(得分:0)

将以下内容添加到services.yml

wsse.security.authentication.provider:
    class: OBCarsTest2Bundle\Security\Authentication\Provider\WsseProvider
    arguments: ["", "%kernel.cache_dir%/security/nonces"]

wsse.security.authentication.listener:
    class: OBCarsTest2Bundle\Security\Firewall\WsseListener
    arguments: ["@security.token_storage","@security.authentication.manager"]

之后你应该将这个方法添加到你的bundle类中(在你的情况下,在OBCarsTest2Bundle包文件夹中的OBCarsTest2Bundle.php)

function build(ContainerBuilder $container)
{
    parent::build($container);

    $extension = $container->getExtension('security');
    $extension->addSecurityListenerFactory(new WsseFactory());
}

你的OBCarsTest2Bundle.php类看起来应该是这样的;

<?php

namespace OBCarsTest2Bundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use OBCarsTest2Bundle\Security\Factory\WsseFactory;

class OBCarsTest2Bundle extends Bundle
{
function build(ContainerBuilder $container)
    {
    parent::build($container);

    $extension = $container->getExtension('security');
    $extension->addSecurityListenerFactory(new WsseFactory());
    }
}

我希望有所帮助。