Токен аутентификации Symfony потерян после перенаправления

Прежде всего, не копируйте этот код. Скоро это будет доступно на моем гитхабе. (Я обновлю этот пост на всякий случай, если он кому-то понадобится)

Привет. Я пытаюсь использовать Steam для подключения в моем приложении. Итак, я попытался создать настраиваемого поставщика пользователей и настраиваемую проверку подлинности. После того, как я нажму кнопку «Войти», мой пользователь (я добавил его сам) загружается из базы данных, и я перенаправляюсь на свою настраиваемую страницу. На этой странице моя панель инструментов отладки сообщает мне, что я аутентифицирован с помощью моего собственного токена и брандмауэра. Если я перейду на другую страницу, например «/ search», моя панель инструментов отладки сообщает мне, что я больше не аутентифицирован ...

Что я делаю неправильно?

Я использую Symfony 4.0.6. Спасибо !

P.S .: Этот сценарий вдохновлен этим: https://github.com/SirWaddles/SteamAuthBundle

P.P.S: Если я пропустил какой-либо файл, а он вам нужен, ответьте.

P.P.P.S: Я думаю, это проблема с serialize() и unserialize(), но я точно не знаю.

Player.php

<?php

namespace App\Entity;

use App\Service\SteamAuth\User\SteamUserInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * Class Player
 * @package App\Entity
 *
 * @ORM\Entity(repositoryClass = "App\Repository\PlayerRepository")
 * @ORM\Table(name = "players")
 */
class Player implements UserInterface, SteamUserInterface, AdvancedUserInterface, EquatableInterface, \Serializable
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type = "integer")
     *
     * @var int
     */
    private $id;

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

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

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

    /**
     * @ORM\Column(type = "string", length=255, nullable=true)
     *
     * @var null|string
     */
    private $avatar;

    /**
     * @var array
     *
     * @ORM\Column(type = "array")
     */
    private $roles;

    /**
     * @var \DateTime|null
     *
     * @ORM\Column(type = "datetime", nullable=true)
     */
    private $lastSync;

    /**
     * @var bool
     *
     * @ORM\Column(type = "boolean")
     */
    private $enabled;

    /**
     * @return int
     */
    public function getId(): int
    {
        return $this->id;
    }

    /**
     * @param int $id
     *
     * @return Player
     */
    public function setId(int $id): Player
    {
        $this->id = $id;

        return $this;
    }

    /**
     * @return string
     */
    public function getUsername(): string
    {
        return $this->username;
    }

    /**
     * @param string $username
     *
     * @return Player
     */
    public function setUsername(string $username): Player
    {
        $this->username = $username;

        return $this;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     *
     * @return Player
     */
    public function setName(string $name): Player
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @return array
     */
    public function getRoles()
    {
        return $this->roles;
    }

    /**
     * @param array $roles
     * @return Player
     */
    public function setRoles(array $roles): Player
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @return \DateTime|null
     */
    public function getLastSync(): ?\DateTime
    {
        return $this->lastSync;
    }

    /**
     * @param \DateTime|null $lastSync
     * @return Player
     */
    public function setLastSync(?\DateTime $lastSync): Player
    {
        $this->lastSync = $lastSync;

        return $this;
    }

    /**
     * @return null|string
     */
    public function getSalt()
    {
        return null;
    }

    /**
     * @param string $password
     * @return Player
     */
    public function setPassword(string $password): Player
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @return null|string
     */
    public function getAvatar(): ?string
    {
        return $this->avatar;
    }

    /**
     * @param null|string $avatar
     *
     * @return Player
     */
    public function setAvatar(?string $avatar): Player
    {
        $this->avatar = $avatar;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function eraseCredentials()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function isAccountNonExpired()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isAccountNonLocked()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isCredentialsNonExpired()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isEnabled()
    {
        return $this->enabled;
    }

    /**
     * @param bool|null $enabled
     * @return Player
     */
    public function setEnabled(?bool $enabled): Player
    {
        $this->enabled = $enabled;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isEqualTo(UserInterface $user)
    {
        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize([
            $this->id,
            $this->username,
            $this->name,
            $this->avatar,
            $this->password,
            $this->enabled
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($data)
    {
        list($this->id, $this->username, $this->name, $this->avatar, $this->password, $this->enabled) = unserialize($data);
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->getUsername() ?? '-';
    }
}

SteamToken.php

<?php

namespace App\Service\SteamAuth\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

/**
 * Class SteamToken
 * @package App\Service\SteamAuth\Token
 */
class SteamToken extends AbstractToken
{
    /**
     * {@inheritdoc}
     */
    public function __construct(array $roles = [])
    {
        parent::__construct($roles);

        $this->setAuthenticated(count($roles) > 0);
    }

    /**
     * {@inheritdoc}
     */
    public function setAttributes(array $attributes)
    {
        foreach ($attributes as $key => $attribute) {
            $key = str_replace("openid_", "openid.", $key);
            $this->setAttribute($key, $attribute);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getCredentials()
    {
    }


    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize([
            $this->getUser(),
            $this->isAuthenticated(),
            $this->getAttributes()
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($data)
    {
        $data = unserialize($data);

        $this->setUser($data[0]);
        $this->setAuthenticated($data[1]);
        $this->setAttributes($data[2]);
    }
}

SteamListener.php

<?php

namespace App\Service\SteamAuth\Firewall;

use App\Service\SteamAuth\Token\SteamToken;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;

/**
 * Class SteamListener
 * @package App\Service\SteamAuth
 */
class SteamListener implements ListenerInterface
{
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @var AuthenticationManagerInterface
     */
    private $authentication;

    /**
     * SteamListener constructor.
     *
     * @param TokenStorageInterface $tokenStorage
     * @param AuthenticationManagerInterface $authentication
     */
    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authentication)
    {
        $this->tokenStorage = $tokenStorage;
        $this->authentication = $authentication;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        if ($request->get('_route') === 'login_check') {
            $token = new SteamToken();

            $token->setUser(str_replace("http://steamcommunity.com/openid/id/", "", $request->query->get('openid_claimed_id')));
            $token->setAttributes($request->query->all());

            try {
                $authToken = $this->authentication->authenticate($token);
                $this->tokenStorage->setToken($authToken);

                return;
            } catch (AuthenticationException $exception) {

            }
        }

        $response = new Response();
        $response->setStatusCode(Response::HTTP_FORBIDDEN);
        $event->setResponse($response);

        return;
    }
}

SteamProvider.php

<?php

namespace App\Service\SteamAuth\Authentication;

use App\Service\SteamAuth\Token\SteamToken;
use GuzzleHttp\Client;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

/**
 * Class SteamProvider
 * @package App\Service\SteamAuth\Provider
 */
class SteamProvider implements AuthenticationProviderInterface
{
    /**
     * @var UserProviderInterface
     */
    private $userProvider;

    /**
     * @var Client
     */
    private $client;

    /**
     * SteamProvider constructor.
     *
     * @param UserProviderInterface $userProvider
     * @param Client                $client
     */
    public function __construct(UserProviderInterface $userProvider, Client $client)
    {
        $this->userProvider = $userProvider;
        $this->client       = $client;
    }

    /**
     * {@inheritdoc}
     */
    public function authenticate(TokenInterface $token)
    {
        if ($token->getAttribute('openid.ns') !== "http://specs.openid.net/auth/2.0") {
            throw new AuthenticationException("Invalid token !");
        }

        $checkAuth                = $token->getAttributes();
        $checkAuth['openid.mode'] = 'check_authentication';

        $response = $this->client->request('GET', 'login', ['query' => $checkAuth]);

        if ((string)$response->getBody() === "ns:http://specs.openid.net/auth/2.0\nis_valid:true\n") {
            $user = $this->userProvider->loadUserByUsername($token->getUsername());

            $authToken = new SteamToken($user->getRoles());
            $authToken->setUser($user);

            return $authToken;
        }

        throw new AuthenticationException("Invalid token !");
    }

    /**
     * {@inheritdoc}
     */
    public function supports(TokenInterface $token)
    {
        return $token instanceof SteamToken;
    }
}

SteamUserProvider.php

<?php

namespace App\Service\SteamAuth\User;

use App\Entity\Player;
use App\Service\SteamAuth\SteamUserService;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

/**
 * Class SteamUserProvider
 * @package App\Service\SteamAuth\User
 */
class SteamUserProvider implements UserProviderInterface
{
    /**
     * @var EntityManager
     */
    private $entityManager;

    /**
     * @var string
     */
    private $userClass;

    /**
     * @var SteamUserService
     */
    private $userService;

    /**
     * SteamUserProvider constructor.
     *
     * @param EntityManager    $entityManager
     * @param SteamUserService $userService
     * @param string           $userClass
     */
    public function __construct(EntityManager $entityManager, SteamUserService $userService, string $userClass)
    {
        $this->entityManager = $entityManager;
        $this->userService   = $userService;
        $this->userClass     = $userClass;
    }

    /**
     * {@inheritdoc}
     */
    public function loadUserByUsername($username)
    {
        $repository = $this->entityManager->getRepository($this->userClass);
        $player     = $repository->findOneBy(['username' => $username]);

        if (!$player) {
            /**
             * @var $player Player
             */
            $player = new $this->userClass();
            $player->setUsername($username);
            $player->setPassword(md5(random_bytes(15)));
            $player->setRoles(['ROLE_USER']);
            $player->setEnabled(1);

            $player = $this->userService->updateUserEntry($player);

            $this->entityManager->persist($player);
            $this->entityManager->flush($player);
        }

        /// if last update....

        return $player;
    }

    /**
     * {@inheritdoc}
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof SteamUserInterface) {
            throw new UnsupportedUserException("User not supported!");
        }

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

    /**
     * {@inheritdoc}
     */
    public function supportsClass($class)
    {
        return $class === $this->userClass;
    }
}

services.yaml

services:
    ...
    # Aliases
    GuzzleHttp\Client: '@eight_points_guzzle.client.login'

    # Log In System
    app.steam_user.service:
        class: App\Service\SteamAuth\SteamUserService
        arguments: ['@eight_points_guzzle.client.steam', '%steam_api_key%']

    app.steam_user.provider:
        class: App\Service\SteamAuth\User\SteamUserProvider
        arguments:
            $entityManager: '@doctrine.orm.default_entity_manager'
            $userService: '@app.steam_user.service'
            $userClass: '%steam_user_class%'

    app.steam.provider:
        class: App\Service\SteamAuth\Authentication\SteamProvider
        arguments:
            $userProvider: '@app.steam_user.provider'
            $client: '@eight_points_guzzle.client.login'

    app.steam.listener:
        class: App\Service\SteamAuth\Firewall\SteamListener

security.yaml

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        steamauth:
            id: app.steam_user.provider
    firewalls:
        steam_auth:
            pattern: ^/login_check
            stateless: true
            steam: ~
            form_login:
                csrf_token_generator: security.csrf.token_manager
            remember_me:
                secret: '%kernel.secret%'
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Обновлено: Если я dump($this->get('session')->all()) и эта страница перед обновлением, я получаю следующее: "_security_steam_auth" => "C:38:"App\Service\SteamAuth\Token\SteamToken":40:{a:4:{i:0;N;i:1;b:0;i:2;a:0:{}i:3;a:0:{}}}"

Пожалуйста, посмотрите: blog.vandenbrand.org/2012/06/19/…

Weenesta - Mathieu Dormeval 21.03.2018 13:37

@MathieuDormeval Я читал это несколько раз ...

GasKa 21.03.2018 17:10

Что если вы установите stateless=false на security.yml? Если он не имеет состояния, cookie отсутствует, поэтому, если при возврате выполняется перенаправление, браузер не знает, кто вы.

Pipe 22.03.2018 23:11

Если вы проверите localStorage, есть ли на нем ваш сохраненный token? Потому что, если вы не можете найти его после страницы входа, это может быть потому, что вы его никогда не сохраняли.

l.g.karolos 27.03.2018 18:00

@Pipe нет, все равно не работает.

GasKa 27.03.2018 20:48

@ l.g.karolos Я знаю, это может показаться глупым, но как мне получить доступ к localStorage. Я искал в Google об этом, и это касается кеша на стороне клиента, верно? Не могли бы вы рассказать мне больше, пожалуйста?

GasKa 27.03.2018 20:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
2
6
1 349
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

У меня была похожая проблема.

В процессе сериализации у меня отсутствовал атрибут: isActive Я никогда не понимаю, почему, но когда этот атрибут является моим процессом сериализации / десериализации, он работает нормально, а когда нет, он вообще не работает.

вот мой источник: https://github.com/matthieuleorat/documentManager/blob/master/src/Entity/User.php#L253

Документ: http://symfony.com/doc/current/security/entity_provider.html#security-serialize-equatable

Надеюсь, это поможет.

Привет. Я посмотрю. Если это сработает, вы получите очки! Спасибо ! : D

GasKa 21.03.2018 17:09

У вас нет специального токена, не так ли?

GasKa 21.03.2018 18:42

У вас включен module_suhosin7?

У нас возникли проблемы на сеансе с включенным suhosin7. Фактически, он добавляет некоторые правила для управления сеансами и файлами cookie.

Если он включен, попробуйте отключить его и проверьте, работает ли он.

Существует известная проблема с шифрованием сеанса с помощью suhosin7: https://github.com/sektioneins/suhosin7/issues/21

Привет. Нет, я не использую Сухосин.

GasKa 22.03.2018 16:49

Эти строки в SteamListener.php предотвращают работу любых других маршрутов.

$request = $event->getRequest();

if ($request->get('_route') === 'login_check') {
    [...]
}

$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response);

Безопасность Функция handle () брандмауэра вызывается каждый раз, когда вы переходите на страницу, поэтому, если вы установите статус других страниц как запрещенные, вы не сможете перемещаться по другим страницам, кроме входа в систему.

Это должно сработать, если удалить его. Сообщите мне, если проблема решена

Хорошо, я попытался удалить эту строку. Ничего не изменилось. Я удалил все, кроме блока $request = $event->getRequest() и try-catch, потому что мне необходимо получить некоторые атрибуты из url. Обновлено: О тех строках с запрещенным ответом, вот как Symfony объясняет, как создать настраиваемого поставщика аутентификации. (symfony.com/doc/current/security/…)

GasKa 27.03.2018 20:57

В их случае они всегда пытаются аутентифицировать токен, чего не было в вашем коде, поэтому я подумал, что с этими строками что-то не так.

Med 28.03.2018 11:15
Ответ принят как подходящий

После долгой работы я решил снова исследовать пакет Steam Authentication и нашел его для Symfony 4, и я подтверждаю, что он работает.

Ссылка: https://github.com/knojector/SteamAuthenticationBundle

Другие вопросы по теме