Symfony 2中的多个身份验证提供程序用于单个防火墙

时间:2015-11-02 13:48:45

标签: php symfony session authentication

我有一个Symfony 2.7.6项目,其中包含自定义简单表单身份验证提供程序,并支持记住我功能以及非个性化功能。一切都按预期工作。

但是,我想引入另一个身份验证提供程序,该提供程序将允许使用两个HTTP标头进行身份验证的会话状态(例如API-Client-IdAPI-Client-Token)与第三方应用程序无关的请求。

我创建了一个简单的Pre-Auth身份验证提供程序,用于验证这些头字段,并在成功时创建带有空用户实例的身份验证令牌。

但是,看起来Symfony正在尝试使用会话记住这些API身份验证,因此我在第二个请求中收到以下错误:“您无法从EntityUserProvider刷新不包含标识符的用户用户对象必须使用Doctrine映射的自己的标识符进行序列化。“。

我可以在防火墙配置中设置stateless: true标志以禁用会话支持,但它会为两个身份验证提供程序禁用它。

那么,如何使用我的Simple Form身份验证器保留现有功能,然后创建另一层身份验证以用于单个无状态API请求?

我不确定我的方法在概念上是否正确。我很乐意接受任何建议,并会在第一次请求时提供任何相关信息。

这是我的security.yml配置:

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

        main:
            pattern: ^/
            anonymous: ~
            form_login:
                login_path: app.login
                check_path: app.session.sign_in
                username_parameter: username
                password_parameter: password
                success_handler: app.security.login_handler
                failure_handler: app.security.login_handler
                require_previous_session: false
            logout:
                path: app.session.sign_out
                invalidate_session: false
                success_handler: app.security.logout_success_handler
            # Simple form auth provider
            simple_form:
                authenticator: app.security.authenticator.out_service
            # Token provider
            simple_preauth:
                authenticator: app.security.authenticator.api_client               
            remember_me:
                name: "%app.session.remember_me.name%"
                key: "%secret%"
                lifetime: 1209600 # 14 days
                path: /
                domain: ~
                always_remember_me: true
            switch_user: { role: ROLE_ADMIN }

    access_control:
        - { path: ^/login,                        roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/recover-password,             roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: /,                              roles: IS_AUTHENTICATED_REMEMBERED  }

    providers:
        main:
            entity:
                class: App\AppBundle\Model\User
                property: id
    encoders:
        App\AppBundle\Model\User: plaintext

    role_hierarchy:
        ROLE_ADMIN: [ROLE_USER, ROLE_ACTIVE]
        ROLE_API_CLIENT: ~
        ROLE_USER: ~
        ROLE_ACTIVE: ~

ApiClientAuthenticator.php

<?php

namespace App\AppBundle\Security;

use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use App\AppBundle\Model\User;


class ApiClientAuthenticator implements SimplePreAuthenticatorInterface
{
    /** @var  LoggerInterface */
    protected $logger;

    /** @var  array */
    protected $clients;


    /**
     * @param array $clients
     */
    public function __construct(array $clients)
    {
        $this->clients = $clients;
    }    

    public function createToken(Request $request, $providerKey)
    {    
        $clientId = $request->headers->get('Api-Client-Id');
        $clientSecret = $request->headers->get('Api-Client-Secret');

        if (!$clientId || !$clientSecret) {
            return null;
        }

        return new PreAuthenticatedToken(
            'anon.',
            [$clientId, $clientSecret],
            $providerKey
        );
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        list ($clientId, $clientSecret) = $token->getCredentials();    

        $foundClient = null;
        foreach ($this->clients as $client) {
            if ($client['id'] == $clientId) {
                if ($client['secret'] == $clientSecret) {
                    $foundClient = $client;
                    break;
                }
            }
        }

        if (!$foundClient) {
            throw new AuthenticationException;
        }

        $user = new User;
        $user->setApiClient(true);
        return new PreAuthenticatedToken(
            $user,
            $foundClient,
            $providerKey,
            ['ROLE_API_CLIENT']
        );
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return ($token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey);
    }    
}

0 个答案:

没有答案