如何使用 Symfony4.3 设置 HWIOAuthBundle?

How to setup HWIOAuthBundle with Symfony4.3?

我在 SF4.3 上有一个项目,但我没有使用 FOSUserBundle。如何设置 HWIOAuthBundle?我现在的配置:

security.yaml

main:
            anonymous: ~
            oauth:
                resource_owners:
                    facebook:           "/login/check-facebook"
                    google:             "/login/check-google"
                    #my_custom_provider: "/login/check-custom"
                    #my_github:          "/login/check-github"
                login_path:        /login
                use_forward:       false
                failure_path:      /login
                provider: users
                oauth_user_provider:
                    service: my.oauth_aware.user_provider.service

hwi_oauth.yaml

hwi_oauth:
    # list of names of the firewalls in which this bundle is active, this setting MUST be set
    firewall_names: [main]

    # https://github.com/hwi/HWIOAuthBundle/blob/master/Resources/doc/2-configuring_resource_owners.md
    resource_owners:
        facebook:
            type:                facebook
            client_id:           '%env(FB_ID)%'
            client_secret:       '%env(FB_SECRET)%'
            scope:               "email"
            options:
                display: popup
                csrf: true
        google:
            type:                google
            client_id:           '%env(G_ID)%'
            client_secret:       '%env(G_SECRET)%'
            scope:               "email"

并在 security.yaml

my.oauth_aware.user_provider.service:
    class: HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider
    arguments:
        - '@fos_user.user_manager'
        - ['@fos_user.user_manager', google: google ]

如果我不为 security.yaml 中的用户提供程序使用 FOSUser,则必须不同,如何为我的 User 配置提供程序?

好的,我创建了自己的提供商:

class OAuthUserProvider extends BaseClass {

public $entityManager;

public $userRepository;

public function __construct(
    UserManagerInterface $userManager,
    array $properties,
    UserRepository $userRepository,
    EntityManagerInterface $entityManager
) {
    parent::__construct($userManager, $properties);
    $this->userRepository = $userRepository;
    $this->entityManager = $entityManager;
}

/**
 * {@inheritdoc}
 * @throws \Doctrine\ORM\NonUniqueResultException
 */
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
    $socialID = $response->getUsername();
    $user = $this->userRepository->findByGoogleId($socialID);
    $email = $response->getEmail();
    //check if the user already has the corresponding social account
    if (null === $user) {
        //check if the user has a normal account
        $user = $this->userRepository->findUserByEmail($email);

        if (null === $user || !$user instanceof UserInterface) {
            //if the user does not have a normal account, set it up:
            $user = new User();
            $user->setEmail($email);
            $user->setPlainPassword(md5(uniqid('', true)));
            $user->setActive(true);
        }
        //then set its corresponding social id
        $service = $response->getResourceOwner()->getName();
        switch ($service) {
            case 'google':
                $user->setGoogleID($socialID);
                break;
            case 'facebook':
                $user->setFacebookID($socialID);
                break;
        }
        $em = $this->entityManager;
        $em->persist($user);
        $em->flush();
        //$this->userManager->updateUser($user);
    } else {
        //and then login the user
        $checker = new UserChecker();
        $checker->checkPreAuth($user);
    }

    return $user;
}
}

在我的 services.yaml:

app.provider.oauth:
        class: App\Security\Providers\OAuthUserProvider
        arguments: ['@fos_user.user_manager',{google: googleID, facebook: facebook}]

如果您想使用自定义用户 class(例如 App\Entity\User),解决方案是创建您自己的用户提供程序。如果您希望用户能够使用传统登录表单 以及 HWIOAuthBundle 进行身份验证,那么您可以开始将自己的用户提供程序设为 described here。当然你必须在这两个方法中添加一些代码才能工作(见下面的例子)并且不要忘记设置 providers 部分security.yaml 像这样:

    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            id: App\Security\UserProvider

然后,如果您可以通过新创建的用户提供程序使用传统登录表单进行身份验证并且一切正常,您就可以开始集成 HWIOAuthBundle。开始将 oauth_user_provider 设置指向您自己的 User Provider class。您的主防火墙可能如下所示:

    firewalls:
        main:
            anonymous: true
            oauth:
                resource_owners:
                    facebook: "/login/check-facebook"
                oauth_user_provider:
                    service: App\Security\UserProvider       # HERE YOU GO!
                login_path: /login
                use_forward:       false
                failure_path:      /login
            form_login:
                login_path: /login
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            logout:
                path: app_logout

顺便说一下,您的 App\Security\UserProvider class 应该在服务容器中自动连接。如果不是,您必须手动将服务添加到您的 service.yaml.

如果您现在尝试从 HWIOAuthBundle 使用资源所有者(例如 Facebook 或 Google)登录,那么您将收到错误消息,因为您的用户提供程序 class 必须实现 HWI 的 OAuthAwareUserProviderInterface。快速查看该接口源文件将了解到您必须向 User Provider 添加一个方法: loadUserByOAuthUserResponse(UserResponseInterface $response)。因此,让您的用户提供者 class 实现 HWI OAuthAwareUserProviderInterface 并将该方法添加到 class。非常简单。

下面是我为这个测试用例编写的完整用户提供程序:

<?php

namespace App\Security;

use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
use App\Entity\User;

class UserProvider implements UserProviderInterface, OAuthAwareUserProviderInterface
{
    private $em;
    private $property = 'email';

    public function __construct(EntityManagerInterface $em) {
        $this->em = $em;
    }

    /**
     * @return UserInterface
     */
    public function loadUserByUsername($username)
    {
        $repository = $this->em->getRepository(User::class);

        if (null !== $this->property) {
            $user = $repository->findOneBy([$this->property => $username]);
        } else {
            if (!$repository instanceof UserLoaderInterface) {
                throw new \InvalidArgumentException(sprintf('You must either make the "%s" entity Doctrine Repository ("%s") implement "Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface" or set the "property" option in the corresponding entity provider configuration.', $this->classOrAlias, \get_class($repository)));
            }
            $user = $repository->loadUserByUsername($username);
        }

        if (null === $user) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }

        return $user;
    }

    /**
     * @return UserInterface
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', User::class));
        }

        $repository = $this->em->getRepository(User::class);

        if ($repository instanceof UserProviderInterface) {
            $refreshedUser = $repository->refreshUser($user);
        } else {
            $refreshedUser = $repository->find($user->getId());
            if (null === $refreshedUser) {
                throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($user->getId())));
            }
        }

        return $refreshedUser;
    }

    /**
     * @return UserInterface
     */
    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
    {
        return $this->loadUserByUsername($response->getEmail());
    }

    /**
     * Tells Symfony to use this provider for this User class.
     */
    public function supportsClass($class)
    {
        return User::class === $class;
    }
}

这个用户提供者还没有准备好。例如,如果未知 facebook/google/... 用户尝试登录,它将抛出异常。您必须根据您的需要扩展此示例并彻底测试它!