0

symfony Webサイトのクックブックにほぼ準拠して、外部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;
    }
}
4

1 に答える 1

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;
        }

ファイアウォール(app / *)の下のすべてのURLで、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アクションはファイアウォールの下にあるため、クレデンシャルが正しい場合(外部APIを使用)、listernerのhandleメソッドが初めて呼び出されます。symfonyにin_memoryユーザーの使用を強制しました。ログインのために、そしてcheck_loginアクションが実行されます。次に、ユーザーがファイアウォールの下の別のページにアクセスすると、handleメソッドが呼び出されますが、認証トークンはすでに設定されているため、handleメソッドが返され、すべてが機能します。

外部APIログインが機能するようになりました!

于 2013-04-05T14:39:33.003 に答える