gpt4 book ai didi

php - 在 Symfony2 中使用自定义身份验证提供程序

转载 作者:可可西里 更新时间:2023-10-31 22:47:36 24 4
gpt4 key购买 nike

我正在开发一个 Symfony2 应用程序,它的 API 可用于其他应用程序。我想保护对 API 的访问。对于这部分,我没有问题。

但我必须使此连接可用,而不是使用通常的登录名/密码对,而只使用 API key 。

所以我去了官方网站及其很棒的食谱 creating a custom authentication provider ,正是我需要对自己说的。

该示例不是我需要的,但我决定根据我的需要对其进行调整。

不幸的是我没有成功。

我会给你我的代码,然后我会解释我的问题。

这是我用于创建身份验证提供程序和监听器的工厂:

<?php

namespace Pmsipilot\UserBundle\DependencyInjection\Security\Factory;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;

class ApiFactory implements SecurityFactoryInterface
{
/**
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param string $id
* @param aray $config
* @param string $userProvider
* @param string $defaultEntryPoint
* @return array
*/
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentification.provider.api.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('api.security.authentification.provider'))
->replaceArgument(0, new Reference($userProvider))
;

$listenerId = 'security.authentification.listener.api.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('api.security.authentification.listener'));

return array($providerId, $listenerId, $defaultEntryPoint);
}

/**
* @return string
*/
public function getPosition()
{
return 'http';
}

/**
* @return string
*/
public function getKey()
{
return 'api';
}

/**
* @param \Symfony\Component\Config\Definition\Builder\NodeDefinition $node
* @return void
*/
public function addConfiguration(NodeDefinition $node)
{
}
}

接下来我的监听器代码:

<?php

namespace Pmsipilot\UserBundle\Security\Firewall;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Pmsipilot\UserBundle\Security\WsseUserToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;

class ApiListener implements ListenerInterface
{
protected $securityContext;
protected $authenticationManager;

/**
* Constructor for listener. The parameters are defined in services.xml.
*
* @param \Symfony\Component\Security\Core\SecurityContextInterface $securityContext
* @param \Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface $authenticationManager
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
}

/**
* Handles login request.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* @return void
*/
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();

$securityToken = $this->securityContext->getToken();

if($securityToken instanceof AuthenticationToken)
{
try
{
$this->securityContext->setToken($this->authenticationManager->authenticate($securityToken));
}
catch(\Exception $exception)
{
$this->securityContext->setToken(null);
}
}
}
}

我的身份验证提供商代码:

<?php

namespace Pmsipilot\UserBundle\Security\Authentication\Provider;

use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class ApiProvider implements AuthenticationProviderInterface
{
private $userProvider;

/**
* Constructor.
*
* @param \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider An UserProviderInterface instance
*/
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}

/**
* @param string $username
* @param \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken $token
* @return mixed
* @throws \Symfony\Component\Security\Core\Exception\AuthenticationServiceException|\Symfony\Component\Security\Core\Exception\UsernameNotFoundException
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$user = $token->getUser();
if($user instanceof UserInterface)
{
return $user;
}

try
{
$user = $this->userProvider->loadUserByApiKey($username, $token->getCredentials());

if(!$user instanceof UserInterface)
{
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
}

return $user;
}
catch (\Exception $exception)
{
throw new AuthenticationServiceException($exception->getMessage(), $token, 0, $exception);
}
}

/**
* @param TokenInterface $token
* @return null|\Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
* @throws \Symfony\Component\Security\Core\Exception\AuthenticationServiceException|\Symfony\Component\Security\Core\Exception\BadCredentialsException|\Symfony\Component\Security\Core\Exception\UsernameNotFoundException
*/
function authenticate(TokenInterface $token)
{
$username = $token->getUsername();
if(empty($username))
{
throw new AuthenticationServiceException('No username given.');
}

try
{
$user = $this->retrieveUser($username, $token);

if(!$user instanceof UserInterface)
{
throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
}

$authenticatedToken = new UsernamePasswordToken($user, null, 'api', $user->getRoles());
$authenticatedToken->setAttributes($token->getAttributes());

return $authenticatedToken;
}
catch(\Exception $exception)
{
throw $exception;
}
}

/**
* @param TokenInterface $token
* @return bool
*/
public function supports(TokenInterface $token)
{
return true;
}
}

为了使用这两个对象,我使用了一个 yml 文件来配置它们:

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="pmsipilot.api.security.authentication.factory" class="Pmsipilot\UserBundle\DependencyInjection\Security\Factory\ApiFactory" public="false">
<tag name="security.listener.factory" />
</service>
</services>
</container>

现在是身份验证提供程序代码:

<?php

namespace Pmsipilot\UserBundle\Security\Authentication\Provider;

use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class ApiProvider implements AuthenticationProviderInterface
{
private $userProvider;

/**
* Constructor.
*
* @param \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider An UserProviderInterface instance
*/
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}

/**
* @param string $username
* @param \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken $token
* @return mixed
* @throws \Symfony\Component\Security\Core\Exception\AuthenticationServiceException|\Symfony\Component\Security\Core\Exception\UsernameNotFoundException
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$user = $token->getUser();
if($user instanceof UserInterface)
{
return $user;
}

try
{
$user = $this->userProvider->loadUserByApiKey($username, $token->getCredentials());

if(!$user instanceof UserInterface)
{
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
}

return $user;
}
catch (\Exception $exception)
{
throw new AuthenticationServiceException($exception->getMessage(), $token, 0, $exception);
}
}

/**
* @param TokenInterface $token
* @return null|\Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
* @throws \Symfony\Component\Security\Core\Exception\AuthenticationServiceException|\Symfony\Component\Security\Core\Exception\BadCredentialsException|\Symfony\Component\Security\Core\Exception\UsernameNotFoundException
*/
function authenticate(TokenInterface $token)
{
$username = $token->getUsername();
if(empty($username))
{
throw new AuthenticationServiceException('No username given.');
}

try
{
$user = $this->retrieveUser($username, $token);

if(!$user instanceof UserInterface)
{
throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
}

$authenticatedToken = new UsernamePasswordToken($user, null, 'api', $user->getRoles());
$authenticatedToken->setAttributes($token->getAttributes());

return $authenticatedToken;
}
catch(\Exception $exception)
{
throw $exception;
}
}

/**
* @param TokenInterface $token
* @return bool
*/
public function supports(TokenInterface $token)
{
return true;
}
}

仅供引用我的用户提供商:

<?php

namespace Pmsipilot\UserBundle\Security\Provider;

use Propel\PropelBundle\Security\User\ModelUserProvider;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use \Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;

class ApiProvider extends ModelUserProvider
{
/**
* Constructeur
*/
public function __construct()
{
parent::__construct('Pmsipilot\UserBundle\Model\User', 'Pmsipilot\UserBundle\Proxy\User', 'username');
}

/**
* @param string $apikey
* @return mixed
* @throws \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
*/
public function loadUserByApiKey($apikey)
{
$queryClass = $this->queryClass;
$query = $queryClass::create();

$user = $query
->filterByApiKey($apikey)
->findOne()
;

if(null === $user)
{
throw new UsernameNotFoundException(sprintf('User with "%s" api key not found.', $apikey));
}
$proxyClass = $this->proxyClass;
return new $proxyClass($user);
}
}

对于配置部分我的 security.yml:

security:
factories:
PmsipilotFactory: "%kernel.root_dir%/../src/Pmsipilot/UserBundle/Resources/config/security_factories.xml"

providers:
interface_provider:
id: pmsipilot.security.user.provider
api_provider:
id: api.security.user.provider

encoders:
Pmsipilot\UserBundle\Proxy\User: sha512

firewalls:
assets:
pattern: ^/(_(profiler|wdt)|css|images|js|favicon.ico)/
security: false

api:
provider: api_provider
access_denied_url: /unauthorizedApi
pattern: ^/api
api: true
http_basic: true
stateless: true

interface:
provider: interface_provider
access_denied_url: /unauthorized
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
use_forward: true
default_target_path: /
logout: ~

access_control:
- { path: ^/api, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: SUPER_ADMIN }

哇,好多代码,希望不要太无聊。

我的问题是,我的自定义身份验证提供程序由两个防火墙 apiinterface 调用,而不仅仅是由 api 调用。当然,他们的行为并不如我所愿。

我没有找到任何关于此类问题的信息。我知道我犯了一个错误,否则它会起作用,但我不知道在哪里以及为什么我不知道。

我还找到了this tutorial但它并没有提供更多帮助。

当然,如果有使用其他身份验证提供程序而不是默认提供程序的其他解决方案,请不要犹豫向我建议。

最佳答案

所以我会回答我自己的问题,因为我找到了我的问题的解决方案,我会告诉你我是如何解决的。

我的示例中有一些错误,我理解他们在 Symfony 代码中搜索。

就像Factory类的getKey方法返回的key。我发现我创建的 api 不是我的 security.yml 文件的其他参数,而是 http_basic 的替代品。这就是为什么我在使用两个提供程序而不是一个提供程序时遇到一些麻烦,因为我有两个 key (api 和 http_basic)都使用了一个提供程序。事实上,我认为这是导致该问题的原因。

为了简单起见,我遵循 Symfony 教程,除了 token 类,但我用 Symfony 类的代码替换了新类的代码。我以某种方式重新创建了 Symfony 的 http 基本身份验证,使其可以过载。在这里,我可以做我想做的事,配置一种基于 Symfony 的不同类型的 http 身份验证,但有几处更改。

这个故事对我有帮助,因为我知道理解 Symfony 原则的最好方法是深入研究代码并加以维护。

关于php - 在 Symfony2 中使用自定义身份验证提供程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9065724/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com