Symfony 4.4:如何在功能测试中创建Keycloak身份验证?

时间:2019-11-25 11:55:27

标签: php symfony phpunit keycloak

我正在尝试将Symfony 4.3.6应用程序升级到v4.4。

该应用使用oauth2-client-bundle通过Keycloak对用户进行身份验证。因此,用户永远不会保留在数据库中。 这是安全性配置:

security:
    providers:
        oauth:
            id: knpu.oauth2.user_provider
    firewalls:
        main:
            logout:
                path: app.logout
            anonymous: true
            guard:
                authenticators:
                    - App\Security\KeycloakAuthenticator
    access_control:
        - { path: ^/connect, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        -
          path: ^/
          allow_if: "'127.0.0.1' == request.getClientIp() or is_granted('IS_AUTHENTICATED_FULLY')"

这是KeycloakAuthenticator服务:

<?php
namespace App\Security;

use App\Entity\User; // extends KnpU\OAuth2ClientBundle\Security\User\OAuthUser
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\Provider\KeycloakClient;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class KeycloakAuthenticator extends SocialAuthenticator
{
    private $clientRegistry;
    private $router;

    public function __construct(ClientRegistry $clientRegistry, RouterInterface $router)
    {
        $this->clientRegistry = $clientRegistry;
        $this->router         = $router;
    }

    public function supports(Request $request)
    {
        return 'connect_keycloak_check' === $request->attributes->get('_route');
    }

    public function getCredentials(Request $request)
    {
        return $this->fetchAccessToken($this->getKeycloakClient());
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $keycloakUser = $this->getKeycloakClient()->fetchUserFromToken($credentials);

        $user = new User($keycloakUser->getName(), ['IS_AUTHENTICATED_FULLY', 'ROLE_USER']);
        $user->setEmail($keycloakUser->getEmail())
            ->setName($keycloakUser->getName())
            ->setLocale($keycloakUser->toArray()['locale'] ?? 'fr')
        ;

        return $user;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // On success, let the request continue
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $message = strtr($exception->getMessageKey(), $exception->getMessageData());

        return new Response($message, Response::HTTP_FORBIDDEN);
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse($this->router->generate('app.keycloak_start'), Response::HTTP_TEMPORARY_REDIRECT);
    }

    private function getKeycloakClient(): KeycloakClient
    {
        return $this->clientRegistry->getClient('keycloak');
    }
}

根据official documentation开发了功能测试:

public function testHomePage()
{
        $client = static::createClient();
        $session = $client->getContainer()->get('session');

        $firewallName = 'main';
        $token = new PostAuthenticationGuardToken(new User('foo', ['ROLE_USER']), $firewallName, $roles);

        $session->set('_security_'.$firewallName, serialize($token));
        $session->save();

        $cookie = new Cookie($session->getName(), $session->getId());
        $client->getCookieJar()->set($cookie);

        $client->request('GET', '/');
        $this->assertTrue($client->getResponse()->isSuccessful());
}

直到现在,测试通过,一切都很好。

但是由于我将Symfony升级到4.4,所以方法ControllerTrait::getUser()返回null,并且在运行功能测试时遇到以下错误:

  

错误:在null上调用成员函数getUsername()

除了调用$this->getUser()时照常获得当前用户外,我都是这样。

我尝试在security.token_storage中手动设置令牌,但错误仍然存​​在。 我还尝试通过删除“ security.yaml”的"'127.0.0.1' == request.getClientIp()部分中的allow_if部分来强制进行身份验证,现在响应为“ 307临时重定向到Keycloak”服务。

在这两个版本之间,$this->getUser()的行为是否发生了变化?

谢谢您的帮助

0 个答案:

没有答案