私のプロジェクトから、カスタム認証プロバイダーに関連して完全に機能する次のコード ファイルがあり、authenticate(TokenInterface $token) で要求をインターセプトできます。出口; 画面にメッセージを出力して、ファイル AuthProvider.php に authenticate() が入力されたことを知らせます。
しかし、login_path:/loginをlogin_path:/user/loginに変更し、check_path:/login_checkをcheck_path:/user/login_checkに変更し、routing.yml を変更して UserBundle にプレフィックス /user を付けようとすると、問題が発生します。次に、コードは機能しますが、 authenticate()には入りませんが、「不正な資格情報」ではなく「提示されたパスワードが無効です」と出力されます。カスタム認証プロバイダーが新しい設定で動作するようにするには?
// app/config/security.yml
security:
factories:
- "%kernel.root_dir%/../src/FD/UserBundle/Resources/config/security_factories.yml"
firewalls:
checkpoint:
pattern: ^/
user: true
form_login:
login_path: /login
check_path: /login_check
logout:
path: /logout
target: /
anonymous: ~
encoders:
FD\UserBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
user_provider:
id: user_provider_service
access_control:
- { path: ^/_internal, role: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
- { path: ^/user/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/event/new, roles: ROLE_ADMIN }
- { path: ^/hello, roles: ROLE_USER }
// app/config/config.yml
imports:
- { resource: parameters.ini }
- { resource: security.yml }
- { resource: @UserBundle/Resources/config/services.yml }
framework:
#esi: ~
translator: { fallback: %locale% }
secret: %secret%
charset: UTF-8
router: { resource: "%kernel.root_dir%/config/routing.yml" }
form: true
csrf_protection: false
validation: { enabled: true }
#validation: { enable_annotations: true }
templating: { engines: ['twig'] } #assets_version: SomeVersionScheme
session:
default_locale: %locale%
auto_start: true
# Twig Configuration
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
# Assetic Configuration
assetic:
debug: %kernel.debug%
use_controller: false
# java: /usr/bin/java
filters:
cssrewrite: ~
# closure:
# jar: %kernel.root_dir%/java/compiler.jar
# yui_css:
# jar: %kernel.root_dir%/java/yuicompressor-2.4.2.jar
# Doctrine Configuration
doctrine:
dbal:
default_connection: default
connections:
default:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
mapping_types:
enum: string
set: string
blob: object
orm:
auto_generate_proxy_classes: %kernel.debug%
default_entity_manager: default # The first defined is used if not set
entity_managers:
default:
# The name of a DBAL connection (the one marked as default is used if not set)
connection: ~
mappings: # Required
FDHelloBundle: ~
UserBundle: { type: annotation }
# mappings:
# FDHelloBundle: { type: yml, dir: Resources/config/doctrine/metadata/orm }
# Swiftmailer Configuration
swiftmailer:
transport: %mailer_transport%
host: %mailer_host%
username: %mailer_user%
password: %mailer_password%
jms_security_extra:
secure_controllers: true
secure_all_services: false
services:
fd_hello.twig.extension.debug:
class: Twig_Extension_Debug
tags:
- { name: 'twig.extension' }
user_provider_service:
class: FD\UserBundle\Security\User\UserProvider
// app/config/routing.yml
FDHelloBundle:
resource: "@FDHelloBundle/Resources/config/routing.yml"
prefix: /
FDUserBundle:
resource: "@UserBundle/Controller"
prefix: /user
type: annotation
// src/FD/UserBundle/Controller/LoginController.php
namespace FD\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Security\Core\SecurityContext;
class LoginController extends Controller
{
/**
* @Route("login", name="login")
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
return $this->render('UserBundle:Login:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
/**
* @Route("login_check", name="login_check")
*/
public function loginCheckAction()
{
}
/**
* @Route("logout", name="logout")
*/
public function logoutAction()
{
}
}
// src/FD/UserBundle/DependencyInjection/Security/Factory/UserFactory.php
namespace FD\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 UserFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.user.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('user.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.user.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('user.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'user';
}
public function addConfiguration(NodeDefinition $node)
{}
}
// FD/UserBundle/Resources/config/services.yml
services:
user.security.authentication.provider:
class: FD\UserBundle\Security\Authentication\Provider\AuthProvider
arguments: ['', %kernel.cache_dir%/security/nonces]
user.security.authentication.listener:
class: FD\UserBundle\Security\Firewall\AuthListener
// FD/UserBundle/Resources/config/security_factories.yml
services:
security.authentication.factory.user:
class: FD\UserBundle\DependencyInjection\Security\Factory\UserFactory
tags:
- { name: security.listener.factory }
// FD/UserBundle/Security/Authentication/Provider/AuthProvider.php
namespace FD\UserBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use FD\UserBundle\Security\Authentication\Token\UserToken;
class AuthProvider implements AuthenticationProviderInterface
{
private $userProvider;
private $cacheDir;
public function __construct(UserProviderInterface $userProvider, $cacheDir)
{
$this->userProvider = $userProvider;
$this->cacheDir = $cacheDir;
}
public function authenticate(TokenInterface $token)
{
// $user = $this->userProvider->loadUserByUsername($token->getUsername());
// $userToken = new UserToken();
// $userToken->setUser($user);
echo "it worked"; exit;
$newToken = new UserToken($token->getUser(), $token->getCredentials(), "user", array("ROLE_ADMIN"));
$username = $newToken->getUser();
if (empty($username)) {
throw new BadCredentialsException('Bad credentials :)');
}
return $newToken;
// if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
// $authenticatedToken = new UserToken($user->getRoles());
// $authenticatedToken->setUser($user);
//
// return $authenticatedToken;
// }
}
public function supports(TokenInterface $token)
{
return $token instanceof UserToken;
}
}
// FD/UserBundle/Security/Authenticaion/Token/UserToken.php
namespace FD\UserBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
/**
* UsernamePasswordToken implements a username and password token.
*
*/
class UserToken extends AbstractToken
{
private $credentials;
private $providerKey;
/**
* Constructor.
*
* @param string $user The username (like a nickname, email address, etc.)
* @param string $credentials This usually is the password of the user
* @param string $providerKey The provider key
* @param array $roles An array of roles
*
* @throws \InvalidArgumentException
*/
public function __construct($user, $credentials, $providerKey, array $roles = array())
{
parent::__construct($roles);
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
$this->setUser($user);
$this->credentials = $credentials;
$this->providerKey = $providerKey;
parent::setAuthenticated(count($roles) > 0);
}
/**
* {@inheritdoc}
*/
public function setAuthenticated($isAuthenticated)
{
if ($isAuthenticated) {
throw new \LogicException('Cannot set this token to trusted after instantiation.');
}
parent::setAuthenticated(false);
}
public function getCredentials()
{
return $this->credentials;
}
public function getProviderKey()
{
return $this->providerKey;
}
/**
* {@inheritdoc}
*/
public function eraseCredentials()
{
parent::eraseCredentials();
$this->credentials = null;
}
public function serialize()
{
return serialize(array($this->credentials, $this->providerKey, parent::serialize()));
}
public function unserialize($str)
{
list($this->credentials, $this->providerKey, $parentStr) = unserialize($str);
parent::unserialize($parentStr);
}
}
// FD/UserBundle/Security/Firewall/AuthListener.php
namespace FD\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 Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
use Symfony\Component\Security\Http\HttpUtils;
use FD\UserBundle\Security\Authentication\Token\UserToken;
class AuthListener extends AbstractAuthenticationListener
{
protected $securityContext;
protected $authenticationManager;
protected $httpUtils;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager,
SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $options = array())
{
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, "user", array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'intention' => 'authenticate',
'post_only' => true,
), $options));
}
/**
* Performs authentication.
*
* @param Request $request A Request instance
*
* @return TokenInterface The authenticated token, or null if full authentication is not possible
*
* @throws AuthenticationException if the authentication fails
*/
protected function attemptAuthentication(Request $request)
{
$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);
//$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UserToken($username, $password, $this->providerKey));
}
public function getHttpUtils()
{
return $this->httpUtils;
}
public function setHttpUtils($httpUtils)
{
$this->httpUtils = $httpUtils;
}
}
// src/FD/UserBundle/Security/User/UserProvider.php
namespace FD\UserBundle\Security\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use FD\UserBundle\Entity\User;
class UserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
// make a call to your webservice here
// $userData = ...
// pretend it returns an array on success, false if there is no user
$user = new User();
$user->setUsername($username);
$user->setPassword("1234");
$user->setRoles(array("ROLE_ADMIN"));
return $user;
// if ($userData) {
// // $password = '...';
// // ...
//
// return new WebserviceUser($username, $password, $salt, $roles)
// } else {
// throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
// }
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'FD\UserBundle\Entity\User';
}
}