symfonycorp/connect-bundle

此包已被弃用,不再维护。作者建议使用 symfonycorp/connect 包。

SymfonyConnect SDK 的官方包

资助包维护!
Tidelift

安装数: 8 196

依赖者: 0

推荐者: 0

安全性: 0

星标: 34

关注者: 15

分支: 16

开放问题: 0

类型:symfony-bundle

v6.2.0 2020-07-22 21:04 UTC

README

关于

这是 SymfonyConnect SDK 的官方包。

安装

第1步:使用 Composer 安装 symfony/connect-bundle

$ composer require symfonycorp/connect-bundle

如果您未使用 Symfony Flex,请参考 此包的配方 以启用它。

第2步:配置您的 .env.local 文件

SYMFONY_CONNECT_APP_ID='Your app id'
SYMFONY_CONNECT_APP_SECRET='Your app secret'

使用方法

使用 SymfonyConnect 验证您的用户

第1步:配置安全设置

注意:如果您想持久化用户,请阅读 食谱 部分。

如果您不想持久化用户,您可以使用 ConnectInMemoryUserProvider

# config/packages/security.yaml
security:
    providers:
        symfony_connect:
            connect_memory: ~
    firewalls:
        # [...]

        secured_area:
            pattern: ^/
            symfony_connect:
                check_path: symfony_connect_callback
                login_path: symfony_connect_login
                failure_path: home # need to be adapted to your config, see step 4
                remember_me: false
                provider: symfony_connect
            anonymous: true

您还可以为某些用户加载特定的角色

# config/packages/security.yaml
security:
    providers:
        symfony_connect:
            connect_memory:
                users:
                    90f28e69-9ce9-4a42-8b0e-e8c7fcc27713: "ROLE_CONNECT_USER ROLE_ADMIN"

注意:用户名是用户 uuid。

第2步:向您的模板添加一些链接

您可以生成一个链接到 SymfonyConnect 登录页面

<a href="{{ url('symfony_connect_login') }}">Connect</a>

您还可以指定登录后的目标 URL

<a href="{{ url('symfony_connect_login') }}?target=XXX">Connect</a>

第3步:与用户互动

API 用户可以通过令牌存储获取,您可以通过自动注入 Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage 来获取。

$user = $tokenStorage->getToken()->getApiUser();

如果您使用内置的安全组件,您可以通过自动注入 SymfonyCorp\Connect\Api\Api $api 直接访问根 API

$user = $api->getRoot()->getCurrentUser();

您也可以通过显式提供访问令牌来访问 API 根对象

$accessToken = $tokenStorage->getToken()->getAccessToken();
$api->setAccessToken($accessToken);
$root = $api->getRoot();
$user = $root->getCurrentUser();

第4步:处理失败

在 OAuth 舞蹈过程中可能会发生几个错误,例如用户可以拒绝您的应用程序,或者您在 symfony_connect.yaml 中定义的作用域可能与您在 SymfonyConnect 上创建应用程序时选择的不同。这些失败由默认的 Symfony 失败处理程序处理。

因此,如果发生错误,错误将被存储在会话中(在查询参数上回退),并且用户将被重定向到 security.yaml 中防火墙的 symfony_connect 部分的 failure_path 节指定的路由/路径。

警告:您必须指定failure_path。如果您不指定,用户将被重定向回login_path,这意味着将启动SymfonyConnect身份验证并将用户重定向到SymfonyConnect,这可能导致重定向循环。

这意味着您需要获取身份验证错误(如果有的话)并在视图中显示它。这与您在Symfony中为典型登录表单所做的事情类似(这里我们假设您有一个指向以下控制器的home路由)

// src/Controller/HomeController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;

class HomeController extends AbstractController
{
    /**
     * @Route("/", name="home")
     */
    public function home(Request $request)
    {
        $session = $request->hasSession() ? $request->getSession() : null;

        // get the authentication error if there is one
        if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
        } elseif (null !== $session && $session->has(Security::AUTHENTICATION_ERROR)) {
            $error = $session->get(Security::AUTHENTICATION_ERROR);
            $session->remove(Security::AUTHENTICATION_ERROR);
        } else {
            $error = '';
        }

        return $this->render('home.html.twig', ['error' => $error]);
    }
}

然后调整您的twig模板

{# templates/home.html.twig #}

{% if app.user %}
    Congrats! You are authenticated with SymfonyConnect
{% elseif error %}
    {{ error.messageKey | trans(error.messageData, 'security') }}
{% else %}
    <a href="{{ url('symfony_connect_login') }}">Log in with SymfonyConnect</a>
{% endif %}

食谱

如何持久化用户

步骤 1 - 创建User实体

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use SymfonyCorp\Connect\Api\Entity\User as ConnectApiUser;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements UserInterface
{
    /** @ORM\Column(type="integer") @ORM\Id @ORM\GeneratedValue(strategy="AUTO") */
    private $id;

    /** @ORM\Column(type="string", length=255) */
    private $uuid;

    /** @ORM\Column(type="string", length=255) */
    private $username;

    /** @ORM\Column(type="string", length=255) */
    private $name;

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

    public function updateFromConnect(ConnectApiUser $apiUser)
    {
        $this->username = $apiUser->getUsername();
        $this->name = $apiUser->getName();
    }

    public function getUuid()
    {
        return $this->uuid;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getRoles()
    {
        return ['ROLE_USER'];
    }

    public function getPassword()
    {
    }

    public function getSalt()
    {
    }

    public function eraseCredentials()
    {
    }
}

步骤 2 - 创建存储库

<?php

namespace App\Repository;

use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class UserRepository extends ServiceEntityRepository implements UserProviderInterface
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, User::class);
    }

    public function loadUserByUsername($uuid)
    {
        return $this->findOneByUuid($uuid) ?: new User($uuid);
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('class %s is not supported', get_class($user)));
        }

        return $this->loadUserByUsername($user->getUuid());
    }

    public function supportsClass($class)
    {
        return User::class === $class;
    }
}

不要忘记更新您的数据库。

步骤 3 - 创建事件监听器

<?php

namespace App\EventListener;

use Doctrine\ORM\EntityManagerInterface;
use SymfonyCorp\Connect\Security\Authentication\Token\ConnectToken;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;

class SecurityInteractiveLoginListener implements EventSubscriberInterface
{
    private $em;

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

    public function registerUser(InteractiveLoginEvent $event)
    {
        $token = $event->getAuthenticationToken();

        if (!$token instanceof ConnectToken) {
            return;
        }

        $user = $token->getUser();
        $user->updateFromConnect($token->getApiUser());

        $this->em->persist($user);
        $this->em->flush($user);
    }

    public static function getSubscribedEvents()
    {
        return [
            SecurityEvents::INTERACTIVE_LOGIN => 'registerUser',
        ];
    }
}

步骤 4 - 配置安全

# config/packages/security.yaml
security:
    encoders:
        App\Entity\User: plaintext

    providers:
        symfony_connect:
            id: App\Repository\UserRepository

步骤 5 - 享受

如果您想的话,可以存储更多内容。但不要忘记更新您的应用程序作用域。

许可

此软件包采用MIT许可。