Symfony 5.4 与 OAuth Keycloak 连接错误必须实现 AuthenticatorInterface

问题描述 投票:0回答:1

我正在尝试在 Symfony 5.4 上实现与 Keycloak 的 OAuth 连接,当我显示应用程序的页面时,一切正常,我有 keycloak 登录页面,但在验证后,出现此错误:

Argument 1 passed to Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticator::__construct() must implement interface Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface, instance of App\Security\KeycloakAuthenticator given, called in /var/www/oauth-symfony/vendor/symfony/security-http/Authenticator/Debug/TraceableAuthenticatorManagerListener.php on line 60

显然我尝试添加

implements AuthenticatorInterface
我不得不添加 authenticate()createToken() 方法,但即使这样,实现仍然不起作用。

KeycloakAuthenticator.php

<?php

namespace App\Security;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Class KeycloakAuthenticator
 */
class KeycloakAuthenticator extends SocialAuthenticator
{
    /**
     * ClientRegistry: the OAuth client manager
     * EntityManagerInterface: to read/write in database
     * RouterInterface: read a route/URL
     */
    private $clientRegistry;
    private $em;
    private $router;

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

    public function start(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $authenticationException = null): RedirectResponse
    {
        return new RedirectResponse(
            '/oauth/login', // might be the site, where users choose their oauth provider
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }

    public function supports(Request $request): ?bool
    {
        return $request->attributes->get('_route') === 'oauth_check';
    }

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

    public function getUser($credentials, \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider)
    {
        $keycloakUser = $this->getKeycloakClient()->fetchUserFromToken($credentials);
        //existing user ?
        $existingUser = $this
                            ->em
                            ->getRepository(User::class)
                            ->findOneBy(['keycloakId' => $keycloakUser->getId()]);
        if ($existingUser) {
            return $existingUser;
        }
        // if user exist but never connected with keycloak
        $email = $keycloakUser->getEmail();
        /** @var User $userInDatabase */
        $userInDatabase = $this->em->getRepository(User::class)
            ->findOneBy(['email' => $email]);
        if($userInDatabase) {
            $userInDatabase->setKeycloakId($keycloakUser->getId());
            $this->em->persist($userInDatabase);
            $this->em->flush();
            return $userInDatabase;
        }
        //user not exist in database
        $user = new User();
        $user->setKeycloakId($keycloakUser->getId());
        $user->setEmail($keycloakUser->getEmail());
        $user->setRoles(['ROLE_USER']);
        $this->em->persist($user);
        $this->em->flush();
        return $user;
    }

    public function onAuthenticationFailure(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $exception)
    {
        $message = strtr($exception->getMessageKey(), $exception->getMessageData());

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

    public function onAuthenticationSuccess(Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token, string $providerKey)
    {
        // change "app_homepage" to some route in your app
        $targetUrl = $this->router->generate('dashboard');

        return new RedirectResponse($targetUrl);
    }

    /**
     * @return \KnpU\OAuth2ClientBundle\Client\Provider\KeycloakClient
     */
    private function getKeycloakClient()
    {
        return $this->clientRegistry->getClient('keycloak');
    }
}

安全.yml

security:
    enable_authenticator_manager: true

    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    providers:

        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            entry_point: form_login
            form_login:
                login_path: oauth_login
            custom_authenticator: App\Security\KeycloakAuthenticator


    access_control:
        # - { path: ^/admin, roles: ROLE_ADMIN }
        # - { path: ^/profile, roles: ROLE_USER }
        - { path: ^/dashboard, roles: ROLE_USER }

OAuthController.php

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\Provider\KeycloakClient;

class OAuthController extends AbstractController
{
    /**
     * @Route("/oauth/login", name="oauth_login")
     */
    public function index(ClientRegistry $registry): RedirectResponse
    {
        /**@var KeycloakClient $client */
        $client = $registry->getClient('keycloak');

        return $client->redirect();
    }

    /**
     * @Route("/oauth/callback", name="oauth_check")
     */
    public function check(){}
}
oauth keycloak openid symfony5 authenticator
1个回答
0
投票

您需要替换要通过 OAuth2Authenticator 扩展的类1

在 onAuthenticationSuccess 和 onAuthenticationFailure 上添加输入“: ?Response”

并创建函数“authenticate”来返回 PassportInterface

© www.soinside.com 2019 - 2024. All rights reserved.