我正在尝试使用两种方法在我的网站中进行身份验证:
使用电子邮件地址/密码(有效)
使用Steam(我遇到问题了)
这是我的services.yaml
:
security:
encoders:
App\Entity\User\User:
algorithm: bcrypt
providers:
user_provider:
entity:
class: App\Entity\User\User
steam_provider:
id: App\Security\SteamAuth\User\SteamUserProvider
chain_provider:
chain:
providers: [user_provider, steam_provider]
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
steam:
anonymous: ~
pattern: /steam/login
user_checker: App\Security\User\UserChecker
provider: steam_provider
steam: true
logout:
path: /logout
target: /
classic:
anonymous: true
user_checker: App\Security\User\UserChecker
provider: user_provider
form_login:
provider: user_provider
default_target_path: /servers
use_referer: true
login_path: /login
check_path: /login
username_parameter: login_form[emailAddress]
password_parameter: login_form[password]
remember_me:
remember_me_parameter: login_form[remember_me]
secret: '%kernel.secret%'
lifetime: 1209600 # 2 week in seconds
path: /
logout:
target: /
access_control:
# Users
- { path: '^/steam/login', roles: IS_AUTHENTICATED_ANONYMOUSLY }
与其他服务相比,Steam仅提供OpenID身份验证。没问题,我更新了代码以使用OpenID。
我认为,即使令牌正确,一切也都可以正常工作。主要问题是在重新加载页面之后。身份验证令牌丢失,并且我已通过匿名用户身份验证。如您所见,无论如何,我都会将用户重定向到/login
(如果用户通过了身份验证,我就有逻辑从该页面重定向)。
这是我的代码:
SteamListener.php
<?php declare(strict_types = 1);
namespace App\Security\SteamAuth\Firewall;
use App\Security\SteamAuth\Authentication\Token\SteamUserToken;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
/**
* Class SteamListener
* @package App\Security\SteamAuth\Firewall
*/
class SteamListener
{
/**
* @var AuthenticationManagerInterface
*/
private $authenticationManager;
/**
* @var RouterInterface
*/
private $router;
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* SteamListener constructor.
*
* @param AuthenticationManagerInterface $authenticationManager
* @param TokenStorageInterface $tokenStorage
* @param RouterInterface $router
*/
public function __construct(AuthenticationManagerInterface $authenticationManager, TokenStorageInterface $tokenStorage, RouterInterface $router)
{
$this->authenticationManager = $authenticationManager;
$this->tokenStorage = $tokenStorage;
$this->router = $router;
}
/**
* Try to authenticate user based on SteamID.
*
* @param RequestEvent $event
*/
public function __invoke(RequestEvent $event)
{
$request = $event->getRequest();
$claimedId = str_replace('https://steamcommunity.com/openid/id/', '', $request->query->get('openid_claimed_id'));
try {
$token = new SteamUserToken($claimedId);
$token->setAttributes($request->query->all());
$authToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authToken);
} catch (AuthenticationException $exception) {
$token = $this->tokenStorage->getToken();
if ($token instanceof SteamUserToken) {
$this->tokenStorage->setToken(null);
}
}
$event->setResponse(new RedirectResponse($this->router->generate('login')));
}
}
SteamProvider.php
<?php declare(strict_types = 1);
namespace App\Security\SteamAuth\Authentication\Provider;
use App\Security\SteamAuth\Authentication\Token\SteamUserToken;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpFoundation\Request;
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;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
/**
* Class SteamProvider
* @package App\Security\SteamAuth\Provider
*/
class SteamProvider implements AuthenticationProviderInterface
{
const ACCEPTED_RESPONSE = "ns:http://specs.openid.net/auth/2.0\nis_valid:true\n";
/**
* This actually points to UserRepository.
*
* @var UserProviderInterface
*/
private $userProvider;
/**
* SteamProvider constructor.
*
* @param UserProviderInterface $userProvider
*/
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}
/**
* {@inheritDoc}
*
* Note: Token is an instance of SteamUserToken.
*/
public function authenticate(TokenInterface $token)
{
if (!$user = $this->userProvider->loadUserByUsername($token->getUsername())) {
throw new AuthenticationException('Steam auth is invalid!');
}
if ($token->getAttribute('openid_ns') !== 'http://specs.openid.net/auth/2.0') {
throw new AuthenticationException('Steam token is invalid!');
}
// Validate SteamID before authenticating user.
$checkAuth = $token->getAttributes();
$checkAuth['openid_mode'] = 'check_authentication';
try {
$request = HttpClient::create();
$response = $request->request(Request::METHOD_GET, $checkAuth['openid_op_endpoint'], ['query' => $checkAuth]);
if ($response->getContent() !== self::ACCEPTED_RESPONSE) {
throw new AuthenticationException('Steam token is invalid!');
}
$authToken = new SteamUserToken($token->getUsername(), $user->getRoles());
$authToken->setUser($user);
return $authToken;
} catch (ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $e) {
throw new AuthenticationException('Steam token is invalid!');
}
}
/**
* {@inheritDoc}
*/
public function supports(TokenInterface $token)
{
return $token instanceof SteamUserToken;
}
}
SteamUserToken.php
<?php declare(strict_types = 1);
namespace App\Security\SteamAuth\Authentication\Token;
use App\Entity\User\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Class SteamUserToken
* @package App\Security\SteamAuth\Authentication
*/
class SteamUserToken implements TokenInterface
{
/**
* @var array
*/
private $attributes = [];
/**
* @var bool
*/
private $authenticated = false;
/**
* @var User|null
*/
private $user = null;
/**
* @var array
*/
private $roles = [];
/**
* @var string|null
*/
private $steamId = null;
/**
* SteamUserToken constructor.
*
* @param string $steamId
* @param array $roles
*/
public function __construct(string $steamId, array $roles = [])
{
$this->steamId = $steamId;
$this->roles = $roles;
$this->authenticated = \count($roles) > 0;
}
/**
* {@inheritDoc}
*/
public function getUser()
{
return $this->user;
}
/**
* {@inheritDoc}
* @var $user UserInterface
*/
public function setUser($user)
{
$this->user = $user;
}
/**
* {@inheritDoc}
*/
public function getUsername()
{
return $this->steamId;
}
/**
* {@inheritDoc}
*/
public function getRoles()
{
if (!$this->user) {
return [];
}
return $this->roles;
}
/**
* @param array $roles
*/
public function setRoles(array $roles = [])
{
$this->roles = $roles;
}
/**
* {@inheritDoc}
*/
public function getRoleNames()
{
return array_map(function ($role) {return $role;}, $this->user ? $this->user->getRoles() : []);
}
/**
* {@inheritDoc}
*/
public function getCredentials()
{
return '';
}
/**
* {@inheritDoc}
*/
public function isAuthenticated()
{
return $this->authenticated;
}
/**
* {@inheritDoc}
*/
public function setAuthenticated($authenticated)
{
$this->authenticated = $authenticated;
}
/**
* {@inheritDoc}
*/
public function eraseCredentials()
{
if ($this->getUser() instanceof UserInterface) {
$this->getUser()->eraseCredentials();
}
}
/**
* {@inheritDoc}
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
* {@inheritDoc}
*/
public function __serialize(): array
{
return [$this->attributes, $this->authenticated, $this->steamId, $this->roles, $this->user];
}
/**
* {@inheritDoc}
*/
public function unserialize($serialized)
{
$this->__unserialize(unserialize($serialized));
}
/**
* {@inheritDoc}
*/
public function __unserialize(array $data): void
{
[$this->attributes, $this->authenticated, $this->steamId, $this->roles, $this->user] = $data;
}
/**
* {@inheritDoc}
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* {@inheritDoc}
*/
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
}
/**
* {@inheritDoc}
*/
public function hasAttribute($name)
{
return \array_key_exists($name, $this->attributes);
}
/**
* {@inheritDoc}
*/
public function getAttribute($name)
{
if (!\array_key_exists($name, $this->attributes)) {
throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name));
}
return $this->attributes[$name];
}
/**
* {@inheritDoc}
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* {@inheritDoc}
*/
public function __toString()
{
if (!$this->user) {
return '-';
}
return $this->user->getUsername();
}
}
如果在return;
内设置$this->tokenStorage->setToken($authToken)
之后写SteamListener
,我会收到一个错误,但这是奇怪的事情:我在该页面中已正确验证(请参阅附上照片)。否则,我将被重定向到登录页面。 (我没有在该方法中引发错误,而是调用了$this->getUser()
并返回了经过身份验证的用户。而且,我尝试将用户重定向到另一个页面,问题仍然存在。)
编辑:这是我的控制台日志:
2019-10-30T14:32:47+01:00 [debug] Stored the security token in the session.
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Sonata\BlockBundle\Cache\HttpCacheHandler::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\EventListener\ResponseListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\DataCollector\RequestDataCollector::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\Security\Http\RememberMe\ResponseListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\EventListener\DisallowRobotsIndexingListener::onResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.response" to listener "Symfony\Component\HttpKernel\EventListener\StreamedResponseListener::onKernelResponse".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.finish_request" to listener "Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelFinishRequest".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.finish_request" to listener "Symfony\Component\HttpKernel\EventListener\SessionListener::onFinishRequest".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.finish_request" to listener "Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelFinishRequest".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.finish_request" to listener "Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::onKernelFinishRequest".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.finish_request" to listener "Symfony\Component\HttpKernel\EventListener\LocaleAwareListener::onKernelFinishRequest".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.terminate" to listener "Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener::onTerminate".
2019-10-30T14:32:47+01:00 [debug] Notified event "kernel.terminate" to listener "Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelTerminate".
[Wed Oct 30 15:32:47 2019] 127.0.0.1:52654 [302]: /steam/login?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.op_endpoint=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Flogin&openid.claimed_id=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Fid%2F
76561198062372939&openid.identity=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Fid%2F76561198062372939&openid.return_to=http%3A%2F%2Flocalhost%3A8000%2Fsteam%2Flogin&openid.response_nonce=2019-10-30T13%3A31%3A49ZSGUwWiQNPgQRCry3p3AjonwB7rg%3D&openid.assoc_handle=1234567890
&openid.signed=signed%2Cop_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle&openid.sig=3ysTtN3Cc3FbC8b0oWi8ZTCrc0U%3D
2019-10-30T14:32:49+01:00 [info] User Deprecated: The Symfony\Bundle\TwigBundle\Loader\FilesystemLoader class is deprecated since version 4.3 and will be removed in 5.0; use Twig notation for templates instead.
2019-10-30T14:32:49+01:00 [info] Matched route "login".
2019-10-30T14:32:49+01:00 [info] Populated the TokenStorage with an anonymous Token.