2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SymfonyAdvent Calendar 2019

Day 13

Symfony4でログインに失敗した場合にログに出力する

Posted at

はじめに

公式ドキュメントのSecurityHow to Build a Login Formを見ながら、ログインフォームでログインをするサンプルを作成しました。

その続きとして、ログインに失敗した場合に、失敗した人のログを取得しておきたいと思います。

方針

How to Build a Login Formで作成した場合、LoginFormAuthenticator.phpにLoggerをインジェクションして出力したくなります。

これをしちゃうと、認証という機能にロギングという別の機能が混じってしまうので、よろしくないようです。

Symfonyの定石としては、認証終了後にAuthentication Eventsが送信されるので、このイベントを待ち受けて処理をするのがよいようです。

イベントリスナーの作成

イベントリスナークラスの雛形の作成

Events and Event Listenersを読みながらイベントリスナーを作成します。

まず、src配下に、EventListenerディレクトリを作成します。
そのディレクトリ配下に、LoginLoggingListenerクラスを作成します。

image.png

以下が、LoginLoggingListenerクラスの骨組みになります。
EventSubscriberInterfaceを継承します。

/src/EventListener/LoginLoggingListener.php
<?php

namespace App\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class LoginLoggingListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        // TODO: Implement getSubscribedEvents() method.
    }
}

リッスンするイベントを登録する

イベントの購読登録は、Creating an Event Listenerを見ていくと、service.yamlに記載する方法と、Creating an Event Subscriberを実装していく方法があります。

今回は、Event Subscriberを実装していきます。

Authentication Eventsを見ると、ログインに失敗した場合は、security.authentication.failureのイベントが起きますので、これをフックすると良さそうです。

image.png

フック関数として、loggingLoginFailureメソッドを登録します。2つ目の10というのは、このイベントに複数のフック関数を登録する場合の優先順となる番号になります。

/src/EventListener/LoginLoggingListener.php
class LoginLoggingListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            AuthenticationEvents::AUTHENTICATION_FAILURE => [
                ['loggingLoginFailure', 10]
            ]
        ];
    }
}

イベントフック関数の実装

フック関数の実態を作成していきます。
フック関数には、AuthenticationFailureEventが渡されますが、欲しい情報は入っているのでしょうか?

一旦、ddして見てみます。

/src/EventListener/LoginLoggingListener.php
class LoginLoggingListener implements EventSubscriberInterface
{
~~~ 省略 ~~~

    public function loggingLoginFailure(AuthenticationFailureEvent $event):void
    {
   dd($event);
    }
}

こんな感じで、ログインフォームに入力された情報のメールアドレス、パスワードは取れました!
しかし、肝心のクライアントのIPなどは入っていません。

image.png

ControllerのようにRequestを取得すればよいかと試しましたが、うまくいきませんでした。

ぐぐってみると、同じことをしたい人がおられました。

Symfony - How to get username and IP address in authentication failure listener?

どうやら、RequestStackをインジェクションするとよいようです。
このあたり、どうして、RequestStackがよいのか、わからないので、誰か詳しい方に教えていただきたいところです。

最終的に、LoggerInterfaceもインジェクトして、以下のようになりました。

/src/EventListener/LoginLoggingListener.php
class LoginLoggingListener implements EventSubscriberInterface
{
    private $logger;
    private $requestStack;

    public function __construct(LoggerInterface $loginAuditLogger, RequestStack $requestStack)
    {
        $this->logger = $loginAuditLogger;
        $this->requestStack = $requestStack;
    }

    public static function getSubscribedEvents()
    {
        return [
            AuthenticationEvents::AUTHENTICATION_FAILURE => [
                ['LoggingLoginFailure', 10]
            ]
        ];
    }

    public function loggingLoginFailure(AuthenticationFailureEvent $event):void
    {
        $hCredentials = $event->getAuthenticationToken()->getCredentials();
        $this->logger->notice(sprintf('[Login Failure] email: %s, password: %s, ip: %s, ua: %s, referer: %s',
            $hCredentials['email'],
            $hCredentials['password'],
            $request->getClientIp(),
            $request->headers->get('user-agent'),
            $request->headers->get('referer')
        ));
    }
}

今回の趣旨と異なるのですが、ログは独自ファイル(login_audit-xxxx.log)に出力しています。

image.png

/var/log/login_audit-2019-12-24.log
[2019-12-24 16:43:15] login_audit.NOTICE: [Login Failure] email: idani@hirotae.com, password: hogehoge, ip: 172.18.0.1, ua: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36, referer: https://localhost/login [] []

まとめ

Symfonyには、様々なイベントがあるので、それを使って、機能を最小化していくと良さそうですね。

そろそろサービスとかバンドルとか、理解しづらい部分がでてきたので、気軽に日本語で相談できるメンターが欲しいところです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?