带开放ID的蒸汽认证(Symfony 4.x)
我尝试使用两种方法在我的网站中进行身份验证:带开放ID的蒸汽认证(Symfony 4.x),symfony,steam,Symfony,Steam,我尝试使用两种方法在我的网站中进行身份验证: 使用电子邮件地址/密码(工作) 使用蒸汽(我有问题的地方) 这是我的服务。yaml: security: encoders: App\Entity\User\User: algorithm: bcrypt providers: user_provider: entity: class: App\Entity\User\User steam_provider: id:
服务。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
在我看来,一切都很好,即使token是正确的。主要问题是在重新加载页面之后。身份验证令牌丢失,我已作为匿名用户进行身份验证。如您所见,无论发生什么情况,我都会将用户重定向到/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')));
}
}
<?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;
}
}
<?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();
}
}
streamusertoken.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')));
}
}
<?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;
}
}
<?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();
}
}
我刚刚浏览了一下,您已经提到了我觉得有点可疑的领域--
SteamListener::\u invoke
方法。对我来说,您总是在该方法的底部设置登录页面的重定向响应,这似乎很奇怪,但基于此,我希望在设置令牌后返回。但是你已经说过它不是。如果在该方法中的$event->setResponse()
调用周围放置一个条件,会有任何影响吗?例如if(!$token){//set redirect response
谢谢您的回复!让我解释一下为什么我使用\u invoke
而不是handle
。从Symfony 4.3开始,该接口被声明为已弃用。现在,让我看看我是否理解正确。您想让我这样做:@mickadoo再添加一件事。如果我添加return;
设置令牌后,我将重定向我的自定义控制器::方法。只有一行:抛出新异常(“如果您到达此页面,请与管理员联系!”)
。我评论了那一行,我正在转储当前用户,猜猜怎么着?它显示了我的帐户!在我更改页面后,所有内容都丢失了。知道吗?可能我的序列化不好?或者防火墙?抱歉,我没有看到你回复我。我对steam身份验证不是100%熟悉,但当我使用OAuth服务时,它将令牌存储在本地客户端上的存储,然后将其包含在每个后续请求中。令牌存储在数据库中,并具有过期日期。您是否正在执行类似操作?@mickadoo我没有使用数据库存储会话令牌。我使用的是Symfony默认使用的系统本地文件。