Symfony 如何在自定义安全包中发生身份验证错误时重定向到注销页面
我正在尝试创建一个自定义安全包 我可以设法让一些东西工作(长时间和努力:-) 除了当我在某个地方出现身份验证错误时,我希望被重新路由到 “注销”页面,而不是登录页面 我认为如果抛出异常,以这种方式更改security.yml将发送到失败路径Symfony 如何在自定义安全包中发生身份验证错误时重定向到注销页面,symfony,Symfony,我正在尝试创建一个自定义安全包 我可以设法让一些东西工作(长时间和努力:-) 除了当我在某个地方出现身份验证错误时,我希望被重新路由到 “注销”页面,而不是登录页面 我认为如果抛出异常,以这种方式更改security.yml将发送到失败路径 security: firewalls: logout: pattern: ^/logout$ security: false secured_area:
security:
firewalls:
logout:
pattern: ^/logout$
security: false
secured_area:
pattern: ^/
my_user_factory: true
form_login:
login_path: /login
check_path: /login_check
failure_path: /logout
由于我的基本代码一直在工作(没有真正的控件),我添加了一个异常抛出来测试身份验证错误
class AuthProvider implements AuthenticationProviderInterface
{
//...
public function authenticate(TokenInterface $token)
{
throw new BadCredentialsException('Bad credentials :)');
}
//...
但我回到了登录页面
以下是跟踪:
// I arrive on the site
UserFactory.getPosition
UserFactory.getKey
UserFactory.getKey
UserFactory.create
CnamtsSecurityExtension.load
Configuration.getConfigTreeBuilder
AcmeSecurityExtension.load
Configuration.getConfigTreeBuilder
AuthProvider.__construct
AuthListener.__construct
AuthProvider.__construct
// Let's login
SecurityController.loginAction
AuthProvider.__construct
AuthListener.__construct
AuthListener.attemptAuthentication
UserToken.__construct
AuthProvider.supports
AuthProvider.authenticate
// The exception is thrown
UserToken.serialize
UserToken.unserialize
UserToken.serialize
AuthProvider.__construct
SecurityController.loginAction
// Back to the login page. I'd like to be on the logout one.
这是全部代码。如果删除“抛出新的BadCredentialsException('Bad credentials:)”;”行
它应该会起作用
束树:
|~src/
| |~Acme/
| | `~SecurityBundle/
| | |~Controller/
| | | |-DefaultController.php
| | | `-SecurityController.php
| | |~DependencyInjection/
| | | |~Security/
| | | | `~Factory/
| | | | `-UserFactory.php
| | | |-AcmeSecurityExtension.php
| | | `-Configuration.php
| | |~Resources/
| | | |~config/
| | | | |-routing.yml
| | | | |-security_factories.yml
| | | | `-services.yml
| | | `~views/
| | | |~Default/
| | | | `-index.html.twig
| | | | `-logout.html.twig
| | | `~Login/
| | | `-login.html.twig
| | |~Security/
| | | |~Authentication/
| | | | |~Firewall/
| | | | | `-AuthListener.php
| | | | |~Provider/
| | | | | `-AuthProvider.php
| | | | `~Token/
| | | | `-UserToken.php
| | | `~User/
| | | |-User.php
| | | `-UserProvider.php
| | |+Tests/
| | `-AcmeSecurityBundle.php
工厂:
<?php
// src/Acme/SecurityBundle/DependencyInjection/Security/Factory/UserFactory.php
namespace Acme\SecurityBundle\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 'my_user_factory';
}
public function addConfiguration(NodeDefinition $node)
{}
}
工厂声明
# Acme/SecurityBundle/Resources/config/security_factories.yml
services:
security.authentication.factory.user:
class: Acme\SecurityBundle\DependencyInjection\Security\Factory\UserFactory
tags:
- { name: security.listener.factory }
服务:
# Acme/SecurityBundle/Resources/config/services.yml
services:
user.security.authentication.provider:
class: Acme\SecurityBundle\Security\Authentication\Provider\AuthProvider
arguments: ["", %kernel.cache_dir%/security/nonces]
user.security.authentication.listener:
class: Acme\SecurityBundle\Security\Authentication\Firewall\AuthListener
arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils]
tags:
- { name: monolog.logger, channel: security }
user_provider_service:
class: Acme\SecurityBundle\Security\User\UserProvider
应用程序参数
总路线
# /app/config/routing.yml
AcmeSecurityBundle:
resource: "@AcmeSecurityBundle/Resources/config/routing.yml"
prefix: /
安全
# /app/config/security.yml
security:
factories:
- "%kernel.root_dir%/../src/Acme/SecurityBundle/Resources/config/security_factories.yml"
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
user_provider:
id: user_provider_service
firewalls:
login:
pattern: ^/login$
security: false
logout:
pattern: ^/logout$
security: false
secured_area:
pattern: ^/
my_user_factory: true
form_login:
login_path: /login
check_path: /login_check
failure_path: /logout
access_control:
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
#- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
控制器和模板
控制器、主页和注销
<?php
// Acme/SecurityBundle/Controller/DefaultController.php
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('AcmeSecurityBundle:Default:index.html.twig',
array('parametres' => print_r($this->container->getParameterBag()->all(), true),
'request' => print_r($this->getRequest(),true)));
}
public function logoutAction()
{
$this->get('security.context')->setToken(null);
$this->getRequest()->getSession()->invalidate();
return $this->render('CnamtsSecurityBundle:Default:logout.html.twig',
array('parametres' => print_r($this->container->getParameterBag()->all(), true),
'request' => print_r($this->getRequest(),true)));
}
}
当抛出AuthenticationException
时,Symfony调用该身份验证提供程序(如果设置了入口点)的入口点类的start
方法,该方法最终返回对登录路径的重定向响应。您必须发出$token->setAuthenticated(false)代码>在引发异常之前
要添加提供者的入口点,UserFactory
应该扩展
有关工厂和入口点的实现,请参见和。因此,我必须创建一个新的入口点。。。食谱中描述的失败路径是什么(如何自定义表单登录)?如果我想使用这个入口点,我必须在services.yml文件中引用它。。。类似于'security.authentication.form\u entry\u point:class:Acme\SecurityBundle\security\EntryPoint\AccessMasterEntryPoint参数:[@kernel,@security.http\u utils,%failure\u path%,“”]`您能告诉我如何引用失败路径(%failure\u path%不好…)您能否检查验证异常后代码是否到达?我想应该是这样的,因为您的AuthListener
已经扩展了AbstractAuthenticationInListener
。。。仅AuthenticationFailureHandlerInterface.php和AuthenticationSuccessHandlerInterface.php我仅在2.0分支(2.0.12)上。这只在2.1版本上可用吗?哦,对不起,我检查了2.1版本。他们将某些部分重构为一个新类:)。你应该检查一下。实际上是同一个代码。是的,是的。但是$this->option['failure\u path']是空的。所以我不再确定我的security.yml是否良好(代码在上面…搜索“securited_区域”)。如果是这样的话。。。我错过了一些东西。
# Acme/SecurityBundle/Resources/config/routing.yml
AcmeSecurityBundle_homepage:
pattern: /
defaults: { _controller: AcmeSecurityBundle:Default:index }
AcmeSecurityBundle_logout:
pattern: /logout
defaults: { _controller: AcmeSecurityBundle:Default:logout }
login:
pattern: /login
defaults: { _controller: AcmeSecurityBundle:Security:login }
login_check:
pattern: /login_check
# Acme/SecurityBundle/Resources/config/security_factories.yml
services:
security.authentication.factory.user:
class: Acme\SecurityBundle\DependencyInjection\Security\Factory\UserFactory
tags:
- { name: security.listener.factory }
# Acme/SecurityBundle/Resources/config/services.yml
services:
user.security.authentication.provider:
class: Acme\SecurityBundle\Security\Authentication\Provider\AuthProvider
arguments: ["", %kernel.cache_dir%/security/nonces]
user.security.authentication.listener:
class: Acme\SecurityBundle\Security\Authentication\Firewall\AuthListener
arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils]
tags:
- { name: monolog.logger, channel: security }
user_provider_service:
class: Acme\SecurityBundle\Security\User\UserProvider
# /app/config/routing.yml
AcmeSecurityBundle:
resource: "@AcmeSecurityBundle/Resources/config/routing.yml"
prefix: /
# /app/config/security.yml
security:
factories:
- "%kernel.root_dir%/../src/Acme/SecurityBundle/Resources/config/security_factories.yml"
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
user_provider:
id: user_provider_service
firewalls:
login:
pattern: ^/login$
security: false
logout:
pattern: ^/logout$
security: false
secured_area:
pattern: ^/
my_user_factory: true
form_login:
login_path: /login
check_path: /login_check
failure_path: /logout
access_control:
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
#- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
<?php
// Acme/SecurityBundle/Controller/DefaultController.php
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('AcmeSecurityBundle:Default:index.html.twig',
array('parametres' => print_r($this->container->getParameterBag()->all(), true),
'request' => print_r($this->getRequest(),true)));
}
public function logoutAction()
{
$this->get('security.context')->setToken(null);
$this->getRequest()->getSession()->invalidate();
return $this->render('CnamtsSecurityBundle:Default:logout.html.twig',
array('parametres' => print_r($this->container->getParameterBag()->all(), true),
'request' => print_r($this->getRequest(),true)));
}
}
{# Acme/SecurityBundle/Resources/views/Default/index.html.twig #}
Hello ! <a href = "{{ path('AcmeSecurityBundle_logout') }}">logout</a>
<pre>
Parametres
{{ parametres }}
Request
{{ request }}
</pre>
Goodbye !
<pre>
Parametres
{{ parametres }}
Request
{{ request }}
</pre>
<?php
// src/Acme/SecurityBundle/Controller/SecurityController.php
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
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('AcmeSecurityBundle:Login:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
}
{# Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form name="loginForm" action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
{#
If you want to control the URL the user is redirected to on success (more details
below)
<input type="hidden" name="_target_path" value="/account" />
#}
<button type="submit">login</button>
</form>