Symfony2的定制身份验证提供不正确的凭据(Symfony2 custom authentica

2019-08-18 00:52发布

我执行使用以下大致symfony的网站上的菜谱外部API自定义验证提供商。 它正确地工作,几乎所有的听众收听登录表单正确,那么它调用身份验证函数返回的认证令牌,问题是,即使我设置了身份验证令牌的securityContextInterface,系统将返回到错误的登录页面证书。 在代码中,我已经使用了什么会是什么?

security.yml

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    login:
        pattern:  ^/app/login$
        security: false

    api_secured:
        provider:   in_memory
        pattern:    ^/app
        form_login:
            login_path:  /app/login
            check_path:  /app/login_check
        logout:
            path:   /app/logout
            target: /
        api:   true

services.yml

api.security.authentication.provider:
    class:  Manuel\Myapp\MyAppBundle\Security\Authentication\Provider\ApiProvider
    arguments: ['', %kernel.cache_dir%/security/nonces]
api.security.authentication.listener:
    class:  Manuel\Myapp\MyAppBundle\Security\Firewall\ApiListener
    arguments: [@security.context, @security.authentication.manager, %api.url%]

ApiFactory.php

namespace Manuel\Myapp\MyAppBundle\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 ApiFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.api.'.$id;
        $container
            ->setDefinition($providerId, new DefinitionDecorator('api.security.authentication.provider'))
            ->replaceArgument(0, new Reference($userProvider))
        ;

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

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

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

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

    public function addConfiguration(NodeDefinition $node)
    {
    }
}

ApiListener.php

namespace Manuel\Myapp\MyAppBundle\Security\Firewall;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken;
use Httpful\Request;

class ApiListener implements ListenerInterface {
    protected $securityContext;
    protected $authenticationManager;
    protected $container;

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $api)
    {
        $this->securityContext = $securityContext;
        $this->authenticationManager = $authenticationManager;
        //Prendo l'url delle api
        //Viene passato da services.yml alla classe
        $this->api = $api;
    }

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

        //Esiste username e password ?
        if(!array_key_exists('_username', $data) || !array_key_exists('_password', $data)) {
            //Ritorna alla pagina di login con bad credentials
            $this->securityContext->setToken(null);
            return;
        }

        //Autentico in remoto
        $username = $data['_username'];
        $password = $data['_password'];

        $response = Request::post($this->api."/token/new.json")
                    ->body(array(
                        'username'=> $username, 
                        'password'=> $password))
                    ->expectsJson()
                    ->sendsForm()
                    ->send(); 
        $decode = json_decode($response);

        //Se esiste allora vado avanti se no muoio
        if(!$decode->success) {
            $this->securityContext->setToken(null);
            return;
        }

        $token = new ApiUserToken();
        $token->setUser(''.$decode->user);
        $token->token = $decode->token;

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

        } catch (AuthenticationException $failed) {
            // ... si potrebbe loggare qualcosa in questo punto
            // Per negare l'autenticazione, pulire il token. L'utente sarà rinviato alla pagina di login.
            $this->securityContext->setToken(null);
            return;

            // Negare l'autenticazione con una risposta HTTP '403 Forbidden'
            //$response = new Response();
            //$response->setStatusCode(403);
            //$event->setResponse($response);

        }
    }
}

如果我写:

$authToken = $this->authenticationManager->authenticate($token);
var_dump($authToken); die();
$this->securityContext->setToken($authToken);

结果是:

object(Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken)#4780 (5) {["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> object(Symfony\Component\Security\Core\User\User)#4782 (7) { ["username":"Symfony\Component\Security\Core\User\User":private]=> string(4) "user" ["password":"Symfony\Component\Security\Core\User\User":private]=> string(15) "10dmao!?postino" ["enabled":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["credentialsNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonLocked":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["roles":"Symfony\Component\Security\Core\User\User":private]=> array(1) { [0]=> string(9) "ROLE_USER" } } ["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(1) { [0]=> object(Symfony\Component\Security\Core\Role\Role)#4779 (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(9) "ROLE_USER" } } ["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> bool(true) ["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(0) { } }

因此,它是正确的。

ApiUserToken.php

namespace Manuel\Myapp\MyAppBundle\Security\Authentication\Token;

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

class ApiUserToken extends AbstractToken
{
    public $token;

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

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

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

ApiProvider.php

namespace Manuel\Myapp\MyAppBundle\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 Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken;

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

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

    public function authenticate(TokenInterface $token)
    {

        //Devo aggiungere utente
        $user = $this->userProvider->loadUserByUsername("user");

        if ($user) {
            $authenticatedToken = new ApiUserToken($user->getRoles());
            $authenticatedToken->setUser($user);

            return $authenticatedToken;
        }

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

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

Answer 1:

我已经解决了修改security.yml

firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            pattern:  ^/app/login$
            security: false

        secured_area:
            pattern:    ^/app
            api: true
            logout:
                path:   /app/logout
                target: /

和ApiListener.php

public function handle(GetResponseEvent $event) {

        if( $this->securityContext->getToken() ){
            return;
        }

因为在防火墙的每个网址上(应用程序/ *)的symfony叫我的听众的handle方法,如果用户已经登录安全令牌已经设置好的,我回

和check_login功能

public function securityCheckAction() {
        // The security layer will NOT intercept this request
        return $this->redirect($this->generateUrl('manuel_myapp_index_after_login'));

check_login是登录表单的动作,check_login动作下的防火墙,所以,我listerner的处理方法将被调用的第一次,如果凭据是正确的(使用我的外部API)我强迫的symfony使用in_memory用户用于登录,比check_login动作将被执行。 然后,当用户访问防火墙下的另一页,处理方法将被召回,但身份验证令牌已经设置好的,所以处理方法将返回所有作品

外部API登录现在的作品!



文章来源: Symfony2 custom authentication provider bad credentials