我正在使用適用於其他應用程序的API使用Symfony2應用程序。 我想確保對API的訪問。對於這部分我沒有問題。在Symfony2中使用自定義身份驗證提供程序
但我必須使這個連接可用不與通常的登錄/密碼對,但只是與一個API密鑰。
所以我去了官方網站和它的真棒食譜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 }
哇這是很多代碼,我希望它不是太無聊。
我在這裏的問題是,我的自定義身份驗證提供由兩個防火牆API和接口,而不是僅僅由API一個叫。 當然,他們不像我想要的那樣行事。
我沒有發現任何關於這樣的問題。 我知道我犯了一個錯誤,否則它會工作,但我在哪裏以及爲什麼我不知道。
我也發現this tutorial但它沒有幫助更多。
當然,不要猶豫,建議我是否有其他解決方案使用另一個身份驗證提供程序而不是默認提供程序。
爲什麼需要API和接口的提供程序?您的ApiProvider沒有任何驗證碼(例如比較用戶傳遞的密鑰) – 2012-02-01 11:37:37
因爲對於我的應用程序的api部分,我只希望用戶使用api密鑰進行身份驗證,而不管他的密碼。 如果API密鑰與用戶匹配,他會自動進行身份驗證。 默認身份驗證提供程序使用用戶名檢查密碼,並且我想要完成此檢查。 – Maxime 2012-02-02 11:22:42
我找到了另一種方法來做我想做的事,但沒有奏效。 我創建了一個「security.authentication.provider.dao.api」作爲id的服務,並且在這個認證提供程序中,我放置了與上面相同的代碼。 但是我遇到了同樣的問題,即使使用接口防火牆也仍然使用它。 我只是想重載Symfony \ Component \ Security \ Core \ Authentication \ Provider \ DaoAuthenticationProvider以避免密碼驗證,它不應該那麼複雜,不是嗎? – Maxime 2012-02-03 09:18:49