Symfony身份验证令牌在重定向后丢失

时间:2018-03-18 18:56:49

标签: symfony authentication steam

首先,不要费心去复制这段代码。这将很快在我的github上提供。 (我会更新这篇文章以防有人需要它)

您好。我正在尝试使用Steam来连接我的应用程序。所以,我试图创建自定义用户提供程序和自定义身份验证。单击“登录”按钮后,我的用户(我自己添加了一个)从数据库加载,然后重定向到我的自定义页面。在该页面中,我的调试工具栏告诉我,我已使用自定义令牌和防火墙进行了身份验证。如果我移动到另一个页面,例如“/ search”,我的调试工具栏告诉我我不再被认证了......

我做错了什么?

我正在使用Symfony 4.0.6。谢谢!

P.S。:这个脚本的灵感来自于这个:https://github.com/SirWaddles/SteamAuthBundle

P.P.S:如果我错过了任何文件,你需要它,请回复。

P.P.P.S:我认为这是void SendNotifacation() { var title = "Winner, Winner, Chicken Dinner"; var message = "You just won a million StackOverflow reputation points"; var intent = new Intent(BaseContext, typeof(MainActivity)); intent.SetAction("ASushiNotification"); intent.PutExtra("MessageFromSushiHangover", message); var pending = PendingIntent.GetActivity(BaseContext, 0, intent, PendingIntentFlags.CancelCurrent); using (var notificationManager = NotificationManager.FromContext(BaseContext)) { Notification notification; if (Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.O) { #pragma warning disable CS0618 // Type or member is obsolete notification = new Notification.Builder(BaseContext) .SetContentTitle(title) .SetContentText(message) .SetAutoCancel(true) .SetSmallIcon(Resource.Drawable.icon) .SetDefaults(NotificationDefaults.All) .SetContentIntent(pending) .Build(); #pragma warning restore CS0618 // Type or member is obsolete } else { var myUrgentChannel = BaseContext.PackageName; const string channelName = "Messages from SushiHangover"; NotificationChannel channel; channel = notificationManager.GetNotificationChannel(myUrgentChannel); if (channel == null) { channel = new NotificationChannel(myUrgentChannel, channelName, NotificationImportance.High); channel.EnableVibration(true); channel.EnableLights(true); channel.SetSound( RingtoneManager.GetDefaultUri(RingtoneType.Notification), new AudioAttributes.Builder().SetUsage(AudioUsageKind.Notification).Build() ); channel.LockscreenVisibility = NotificationVisibility.Public; notificationManager.CreateNotificationChannel(channel); } channel?.Dispose(); notification = new Notification.Builder(BaseContext) .SetChannelId(myUrgentChannel) .SetContentTitle(title) .SetContentText(message) .SetAutoCancel(true) .SetSmallIcon(Resource.Drawable.icon) .SetContentIntent(pending) .Build(); } notificationManager.Notify(1331, notification); notification.Dispose(); } } serialize()的问题,但我不确切知道。

Player.php

unserialize()

SteamToken.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() ?? '-';
    }
}

SteamListener.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]);
    }
}

SteamProvider.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;
    }
}

SteamUserProvider.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;
    }
}

services.yaml

<?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;
    }
}

security.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: # 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())

4 个答案:

答案 0 :(得分:0)

我遇到了类似的问题。

我的序列化过程中缺少一个属性: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

希望有所帮助。

答案 1 :(得分:0)

您是否启用了module_suhosin7?

我们在启用了suhosin7的会话中遇到了问题。 事实上,它增加了一些关于会话和cookie管理的规则。

如果启用,请尝试禁用它并检查它是否有效。

有一个关于使用suhosin7进行会话加密的已知问题: https://github.com/sektioneins/suhosin7/issues/21

答案 2 :(得分:0)

SteamListener.php中的这一行阻止任何其他路由起作用。

$request = $event->getRequest();

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

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

每次导航到页面时都会调用防火墙handle()函数,因此如果将其他页面状态设置为禁止,则无法在登录以外的其他页面上导航。

这应该通过删除它。如果问题解决了,请告诉我

答案 3 :(得分:0)

经过大量的工作,我决定再次研究一个Steam身份验证包,我找到了一个Symfony 4,我确认它有效。

链接:https://github.com/knojector/SteamAuthenticationBundle