5.2のバージョンの下でSymfonyのログイン試み


Symfony 5.2機能は、複数のログインの試みに対して当社のウェブサイトをセキュリティー保護することができます.
これがdocs => Login Throttlingです
しかし、古いバージョンでそれを行うには?これが私が次にあなたに見せるつもりです.

プロジェクト
このプロジェクトについては、ブログを作成しましょう.このブログは登録ユーザーのみにアクセスできます.匿名ユーザーは' login 'と' register 'にのみアクセスできます.
レジスタページでは、悪いボットを避けるために、我々はCAPTCHAを使用するが、1つのCAPTCHAは十分です、我々はログインページのためにそれを必要としません.
プロジェクトの囲炉裏に行ってみよう.
  • プロジェクト
  • を作成します
  • は、make:usermake:authをします
  • あなたの論理をコントローラ
  • に書きなさい
    そして今は働きましょう.

    新しいエンティティを作成する
    それで、我々は我々のウェブサイトに各々の接続を知っていたいです.それぞれの接続については、IPアドレス、メールを使用して日付を知りたい.
    エンティティを作成します.それをlogin試みと呼び、質問に答えます.
    New property name (press <return> to stop adding fields):
     > email
    
     Field type (enter ? to see all types) [string]:
     >
    
    
     Field length [255]:
     >
    
     Can this field be null in the database (nullable) (yes/no) [no]:
     >
    
     updated: src/Entity/LoginAttempt.php
    
     Add another property? Enter the property name (or press <return> to stop adding fields):
     > ipAddress
    
     Field type (enter ? to see all types) [string]:
     >
    
    
     Field length [255]:
     >
    
     Can this field be null in the database (nullable) (yes/no) [no]:
     >
    
     updated: src/Entity/LoginAttempt.php
    
     Add another property? Enter the property name (or press <return> to stop adding fields):
     > date
    
     Field type (enter ? to see all types) [string]:
     > datetime_immutable
    datetime_immutable
    
     Can this field be null in the database (nullable) (yes/no) [no]:
     >
    
    右、現在エンティティを変更できます.必要に応じて変数を入力することができます.すべてのセッターを削除します、私たちはそれらを必要としません、そして、建設者をつくります.コンストラクタではstring $emailstring $ipAddressを使い、日付はnew DatetimeImmutable('now')です.私は、フランスのために時間を得るために、個人的にdate_default_timezone_set('Europe/Paris')を加えました.エンティティは次のようになります.
    class LoginAttempt
    {
        /**
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private ?int $id = null;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private string $email;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private string $ipAddress;
    
        /**
         * @ORM\Column(type="datetime_immutable")
         */
        private \DateTimeImmutable $date;
    
        public function __construct(string $ipAddress, string $email)
        {
            date_default_timezone_set('Europe/Paris');
            $this->ipAddress = $ipAddress;
            $this->email = $email;
            $this->date = new \DateTimeImmutable('now');
        }
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getEmail(): ?string
        {
            return $this->email;
        }
    
        public function getIpAddress(): ?string
        {
            return $this->ipAddress;
        }
    
        public function getDate(): ?\DateTimeImmutable
        {
            return $this->date;
        }
    
    }
    

    LogInformatuenticator
    あなたがmake authをしたならば、あなたはあなたのログインのためにロジックをつくりました、私はこのクラスLogInformatuenticatorと呼ばれました.
    コンストラクターが2つの新しいプライベート変数を追加する前に、最初に$ ipaddressとEntityManagerInterface $managerと呼んで、マネージャをマネージャに追加します.後でipaddressに説明します.
    このクラスでは、2つのメソッドが我々を結び付けます.
  • public function getCredentials(Request $request)
  • public function checkCredentials($credentials, UserInterface $user)

  • getcredentials
    このメソッドはすべての接続を取得します.ログとダイを試して、$ credentialsをダンプすることができます、あなたは使用されるメールにアクセスするでしょう、ハッシュされないパスワードとcsrftoken.パーフェクト!これはまさに我々が望む論理だ.
    したがって、戻り値の直前にこのメソッドで、$ loglogIntryの変数を新しいlogin試み(エンティティ)に等しく追加します.パラメータのIPアドレスとメールに必要なことを忘れないでください.右、あなたの方法を見て、我々は要求へのアクセスを持っている!したがって、最初のパラメータは$request->getClientIp()で、2番目は資格情報です.配列として、$ credentials [ email ]を書く必要があります.
    私はIPアドレスのグローバル変数が欲しいので、プライベート変数$this->ipaddress = $request->getClientIp()を使用します.
    これでデータを永続化してフラッシュするだけです.ログインしてデータベースを見てみてください.
    パーフェクト!
    getCredentialsメソッドは次のようになります.
     public function getCredentials(Request $request)
        {
            $credentials = [
                'email' => $request->request->get('email'),
                'password' => $request->request->get('password'),
                'csrf_token' => $request->request->get('_csrf_token'),
            ];
            $request->getSession()->set(
                Security::LAST_USERNAME,
                $credentials['email']
            );
    
            $newLoginAttempt = new LoginAttempt($request->getClientIp(), $credentials['email']);
    
            $this->ipAddress = $request->getClientIp();
    
            $this->entityManager->persist($newLoginAttempt);
            $this->entityManager->flush();
    
            return $credentials;
        }
    

    特徴を作る
    私たちのCheckCredentialsメソッドのロジックは少し長いので、私は特徴を作成することを好む.それをSecurityLoginInstallTraitと呼んで、それを忘れないために、use TargetPathTrait;の上であなたのLogInformauthicatorのクラスで直接それを使ってください!
    我々の特性に戻って、ASパラメタでSecureのメソッドを作成し始めてください:
  • LoginFittRepository $ repo
  • ストリング$電子メール
  • 文字列ipaddress
  • EntityManagerInterface $ Manager
  • urlgeneratorinterface $ url
  • それから、我々はエラーメッセージで2つの試みからユーザーを防ぎたいです.4番目のトライは、最後の5番目の試みは彼のIPアドレスをブロックします.条件を書き出す
    if(count($repo->recentLoginAttempts($email)) === 2){
                throw new CustomUserMessageAuthenticationException('3 attempts left');
            }
            if(count($repo->recentLoginAttempts($email)) === 3){
                throw new CustomUserMessageAuthenticationException('2 attempts left');
            }
            if(count($repo->recentLoginAttempts($email)) === 4){
                throw new CustomUserMessageAuthenticationException('Be carefull its your last attempt');
            }
    
    WOW待ちました、RecentLoginRetryメソッド、CustomUserMessageAuthenticationExceptionとは何ですか?
    OKで、一歩一歩.
    CustomUserMessageAuthenticationExceptionは自分の名前としてユーザーのカスタムメッセージエラーです.' use文'を使うのを忘れないでください
    ここでLoginHiteRepositoryの中に行き、recentLoginRetryメソッドを作成しましょう.

    方法
    今までの3分からのメールアドレスの接続を知りたいので、書きましょう.
    class LoginAttemptRepository extends ServiceEntityRepository
    {
        const DELAY_IN_MINUTES = 3;
    
        public function __construct(ManagerRegistry $registry)
        {
            parent::__construct($registry, LoginAttempt::class);
        }
    
        public function recentLoginAttempts(string $email)
        {
            date_default_timezone_set('Europe/Paris');
            $timeAgo = new \DateTimeImmutable(sprintf('-%d minutes', self::DELAY_IN_MINUTES));
    
            return $this->createQueryBuilder('l')
                ->andWhere('l.date >= :date')
                ->andWhere('l.email = :email')
                ->setParameter('date', $timeAgo)
                ->setParameter('email', $email)
                ->getQuery()
                ->getResult()
            ;
        }
    
    現在、あなたは我々の状況を理解しなければなりません.例えば、
    if(count($this->repo->recentLoginAttempts($email)) === 3){
                throw new CustomUserMessageAuthenticationException('2 attempts left');
            }
    
    私はすべての回をユーザーが私たちのウェブサイトにマイナス3分の間に接続しようとするとカウントされます.合計が3に等しいならば、彼は新しいカスタムメイドのメッセージを得ます.
    最後の条件を書きましょうが、その前に新しいエンティティを作成する必要があります.

    最後の実体
    ユーザーが3分以下で5回接続しようとするならば、彼のIPアドレスは我々のサイトでブロックされます.したがって、2つのプロパティを持つipblockエンティティを作成します.
  • ipaddress ( string )
  • Blockedat ( datetimepers immutable )
  • すべてのセッターを削除し、string $ipAddressでコンストラクタを作成し、プロパティのブロック化を自動化します.
    エンティティは次のようになります.
    <?php
    
    namespace App\Entity;
    
    use App\Repository\IpBlockedRepository;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass=IpBlockedRepository::class)
     */
    class IpBlocked
    {
        /**
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private ?int $id = null;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private string $ipAddress;
    
        /**
         * @ORM\Column(type="datetime_immutable")
         */
        private \DateTimeImmutable $blockedAt;
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getIpAddress(): ?string
        {
            return $this->ipAddress;
        }
    
        public function __construct(string $ipAddress)
        {
            date_default_timezone_set('Europe/Paris');
            $this->ipAddress = $ipAddress;
            $this->blockedAt = new \DateTimeImmutable('now');
        }
    
        public function getBlockedAt(): ?\DateTimeImmutable
        {
            return $this->blockedAt;
        }
    
    }
    
    
    我々の特性に戻って、我々は現在最後の状態を書くことができます:
    if(count($repo->recentLoginAttempts($email)) === 5){
                $blocked = new IpBlocked($ipAddress);
                $manager->persist($blocked);
                $manager->flush();
                return $url->generate('app_account_blocked');
            }
    
    したがって、ユーザが5回目の接続を試みた場合、$ ipaddressをパラメータとして新しいipblockを呼び出します.
    あなたの特性は次のようになります.
    <?php
    
    
    namespace App\helper;
    
    
    use App\Entity\IpBlocked;
    use App\Repository\LoginAttemptRepository;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
    use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
    
    trait SecurityLoginAttemptTrait
    {
    
        public function secure(LoginAttemptRepository $repo,string $email, string $ipAddress, EntityManagerInterface $manager, UrlGeneratorInterface $url)
        {
            if(count($repo->recentLoginAttempts($email)) === 2){
                throw new CustomUserMessageAuthenticationException('Il vous reste 3 essais');
            }
            if(count($repo->recentLoginAttempts($email)) === 3){
                throw new CustomUserMessageAuthenticationException('Il vous reste 2 essais');
            }
            if(count($repo->recentLoginAttempts($email)) === 4){
                throw new CustomUserMessageAuthenticationException('Attention c\'est votre dernier essai !');
            }
            if(count($repo->recentLoginAttempts($email)) === 5){
                $blocked = new IpBlocked($ipAddress);
                $manager->persist($blocked);
                $manager->flush();
                return $url->generate('app_account_blocked');
            }
        }
    
    }
    

    loginfoに戻る
    checkCredentialsメソッドで作業する前に、新しいプライベートlogintry対面$ repoを作成し、コンストラクターに追加します.
    では、メソッドを呼び出して安全なメソッドを呼び出します.パラメータで必要なことを思い出してください.
  • LoginHandTrepository
  • Eメール
  • IPアドレス
  • EntityManagerInterface
  • urlgeneratorinterface
  • それで、我々は$this->secure($this->repo,$credentials['email'], $this->ipAddress, $this->entityManager, $this->urlGenerator);を書くことができます
    そして、それは、我々のロジックが行われます.ユーザーが3分またはそれの下で5回接続しようとするならば、彼のIPアドレスは我々のデータベースにあります.

    最後に
    我々は、当社のウェブサイトからユーザーのアクセスをブロックしたい.匿名のユーザーが'/login 'と'/register 'にのみアクセスできることを忘れないでください.それから、ログインとレジスターのコントローラに行ってください.IpBlockedRepository $ipBlockedRepoRequest $requestそして、最初の行があなたの状態を書きます.
    if ($ipBlockedRepo->findOneBy(['ipAddress' => $request->getClientIp()])){
                return $this->redirectToRoute('app_account_blocked');
            }
    
    この論理で、ユーザーは接続するか、または登録することができません.もちろん、あなたは新しいルート' AppRange AccounttCountブロックを作成する必要がありますが、それは最も簡単なことです.
    まず、symfony console make:controller AccountBlockedControllerを作ることによって新しいコントローラを作成することから始めます
    次に、IPアドレスがブロックされていない場合、このページにアクセスできないようにします.したがって、RegisterControllerとLoginControllerと同じパラメータを追加してください.if (!$ipBlockedRepo->findOneBy(['ipAddress' => $request->getClientIp()])){
    return $this->redirectToRoute('app_login');
    }
    コントローラは次のようになります.
    <?php
    
    namespace App\Controller;
    
    use App\Repository\IpBlockedRepository;
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    
    class AccountBlockedController extends AbstractController
    {
        /**
         * @Route("/access-blocked", name="app_account_blocked")
         */
        public function index(IpBlockedRepository $ipBlockedRepo, Request $request): Response
        {
            if (!$ipBlockedRepo->findOneBy(['ipAddress' => $request->getClientIp()])){
                return $this->redirectToRoute('app_login');
            }
            return $this->render('account_blocked/index.html.twig', [
                'controller_name' => 'AccountBlockedController',
            ]);
        }
    }
    
    
    そして、あなたのテンプレートをパーソナライズし、ユーザーがなぜ彼のアクセスが禁止されている知っていることを忘れないでください.
    例えば、あなたは言うことができました:「あなたは、このウェブサイトへのアクセスを持っていません.」
    そしてもちろん、あなたの連絡先ページへのアクセスをすべてのユーザーがブロックすることができます.
    あなたがこの記事が好きで、あなたがどんな質問またはどんな改善でもあるならば、ちょっと私に知らせてください
    乾杯!