Symfony2とFOSUserBundleを使用してAJAXログインを作成しようとしている例があります。自分のsuccess_handler
とfailure_handler
をform_login
ファイルのsecurity.yml
ファイルに設定しています。
クラスは次のとおりです。
class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @see \Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener
* @param Request $request
* @param TokenInterface $token
* @return Response the response to return
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => true);
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
/**
* This is called when an interactive authentication attempt fails. This is
* called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* @param Request $request
* @param AuthenticationException $exception
* @return Response the response to return
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => false, 'message' => $exception->getMessage());
$response = new Response(json_encode($result));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
}
これは、成功および失敗の両方の処理に最適ですAJAX=ログイン試行。ただし、有効にすると、標準形式POSTメソッド(非- AJAX)。次のエラーが表示されます。
Catchable Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given
onAuthenticationSuccess
およびonAuthenticationFailure
オーバーライドは、XmlHttpRequests(AJAX要求)に対してのみ実行され、そうでない場合は元のハンドラーに実行を戻すだけです。
これを行う方法はありますか?
TL; DR AJAX成功と失敗に対してJSONレスポンスを返すためにログイン試行を要求しましたが、フォームPOSTによる標準ログインに影響を与えないようにしたいです。
Davidの answer は良いのですが、newbsの詳細が少し欠けているため、これは空白を埋めるためです。
AuthenticationHandlerを作成することに加えて、ハンドラーを作成したバンドルのサービス構成を使用して、サービスとしてセットアップする必要があります。デフォルトのバンドル生成はxmlファイルを作成しますが、私はymlを好みます。 services.ymlファイルの例を次に示します。
#src/Vendor/BundleName/Resources/config/services.yml
parameters:
vendor_security.authentication_handler: Vendor\BundleName\Handler\AuthenticationHandler
services:
authentication_handler:
class: %vendor_security.authentication_handler%
arguments: [@router]
tags:
- { name: 'monolog.logger', channel: 'security' }
次のように、xmlではなくymlを使用するようにDependencyInjectionバンドル拡張を変更する必要があります。
#src/Vendor/BundleName/DependencyInjection/BundleExtension.php
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
次に、アプリのセキュリティ構成で、先ほど定義したauthentication_handlerサービスへの参照を設定します。
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
success_handler: authentication_handler
failure_handler: authentication_handler
namespace YourVendor\UserBundle\Handler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class AuthenticationHandler
implements AuthenticationSuccessHandlerInterface,
AuthenticationFailureHandlerInterface
{
private $router;
public function __construct(Router $router)
{
$this->router = $router;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
// Handle XHR here
} else {
// If the user tried to access a protected resource and was forces to login
// redirect him back to that resource
if ($targetPath = $request->getSession()->get('_security.target_path')) {
$url = $targetPath;
} else {
// Otherwise, redirect him to wherever you want
$url = $this->router->generate('user_view', array(
'nickname' => $token->getUser()->getNickname()
));
}
return new RedirectResponse($url);
}
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
// Handle XHR here
} else {
// Create a flash message with the authentication error message
$request->getSession()->setFlash('error', $exception->getMessage());
$url = $this->router->generate('user_login');
return new RedirectResponse($url);
}
}
}
FOS UserBundleフォームのエラーサポートが必要な場合は、以下を使用する必要があります。
$request->getSession()->set(SecurityContext::AUTHENTICATION_ERROR, $exception);
の代わりに:
$request->getSession()->setFlash('error', $exception->getMessage());
最初の答え。
(もちろん、ヘッダーについて覚えておいてください:Symfony\Component\Security\Core\SecurityContextを使用してください。)
私はこれを完全にjavascriptで処理しました:
if($('a.login').length > 0) { // if login button shows up (only if logged out)
var formDialog = new MyAppLib.AjaxFormDialog({ // create a new ajax dialog, which loads the loginpage
title: 'Login',
url: $('a.login').attr('href'),
formId: '#login-form',
successCallback: function(nullvalue, dialog) { // when the ajax request is finished, look for a login error. if no error shows up -> reload the current page
if(dialog.find('.error').length == 0) {
$('.ui-dialog-content').slideUp();
window.location.reload();
}
}
});
$('a.login').click(function(){
formDialog.show();
return false;
});
}
これがAjaxFormDialogクラスです。残念ながら、今のところjQueryプラグインに移植していません... https://Gist.github.com/16018
両方の場合(Ajaxかどうか)にResponseオブジェクトを返す必要があります。 「else」を追加すると、準備完了です。
デフォルトの実装は次のとおりです。
$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
AbstractAuthenticationListener::onSuccess
AJAXログインフォームを提供する新しいユーザー向けの小さなバンドルを作成しました: https://github.com/Divi/AjaxLoginBundle
form_login認証に置き換える必要がありますajax_form_loginsecurity.yml。
Github課題追跡の新機能をお気軽にご提案ください!
これはOPが尋ねたものではないかもしれませんが、私はこの質問に出会い、他の人が私と同じ問題を抱えているかもしれないと考えました。
AJAX受け入れられた回答に記載されているメソッドを使用してログインし、AngularJSを使用してAJAXリクエストを実行している場合)デフォルトでは機能しません。Angularの_$http
_は、$request->isXmlHttpRequest()
メソッドを呼び出すときにSymfonyが使用するヘッダーを設定しません。このメソッドを使用するには、=で適切なヘッダーを設定する必要がありますAngular request。これは私が問題を回避するためにしたことです:
_$http({
method : 'POST',
url : {{ path('login_check') }},
data : data,
headers: {'X-Requested-With': 'XMLHttpRequest'}
})
_
このメソッドを使用する前に、このヘッダーはCORSではうまく機能しないことに注意してください。 この質問 を参照してください