Прежде всего, не копируйте этот код. Скоро это будет доступно на моем гитхабе. (Я обновлю этот пост на всякий случай, если он кому-то понадобится)
Привет. Я пытаюсь использовать 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:{}}}"
@MathieuDormeval Я читал это несколько раз ...
Что если вы установите stateless=false на security.yml? Если он не имеет состояния, cookie отсутствует, поэтому, если при возврате выполняется перенаправление, браузер не знает, кто вы.
Если вы проверите localStorage, есть ли на нем ваш сохраненный token? Потому что, если вы не можете найти его после страницы входа, это может быть потому, что вы его никогда не сохраняли.
@Pipe нет, все равно не работает.
@ l.g.karolos Я знаю, это может показаться глупым, но как мне получить доступ к localStorage. Я искал в Google об этом, и это касается кеша на стороне клиента, верно? Не могли бы вы рассказать мне больше, пожалуйста?




У меня была похожая проблема.
В процессе сериализации у меня отсутствовал атрибут: 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
У вас нет специального токена, не так ли?
У вас включен module_suhosin7?
У нас возникли проблемы на сеансе с включенным suhosin7. Фактически, он добавляет некоторые правила для управления сеансами и файлами cookie.
Если он включен, попробуйте отключить его и проверьте, работает ли он.
Существует известная проблема с шифрованием сеанса с помощью suhosin7: https://github.com/sektioneins/suhosin7/issues/21
Привет. Нет, я не использую Сухосин.
Эти строки в 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/…)
В их случае они всегда пытаются аутентифицировать токен, чего не было в вашем коде, поэтому я подумал, что с этими строками что-то не так.
После долгой работы я решил снова исследовать пакет Steam Authentication и нашел его для Symfony 4, и я подтверждаю, что он работает.
Ссылка: https://github.com/knojector/SteamAuthenticationBundle
Пожалуйста, посмотрите: blog.vandenbrand.org/2012/06/19/…