1
0

More than 3 years have passed since last update.

【Laravel5.5】メール送信にホワイトリストチェックをいれるよ

Last updated at Posted at 2019-12-29

まえがき

開発環境とかはMailHogをつかって実際に送信しないようにするけど、
ステージングや検証環境みたいな、
 自分や自社宛のだけは実際に送信したい
てときにホワイトリストを作って判別することがあります。
で、先日もLaravelを使っててそんな状況になったので、対応のメモです。

ツール

  • XAMPP v3.2.4
    • PHP 7.3.8
      • Laravel 5.5.45

メール送信処理

今回は、 Illuminate\Mail\Mailable を使ってテンプレートを読み込んで送信する方法で実装しています。

App\Notifications\InvoicePaid
use App\Mail\InvoicePaid as InvoicePaidMail;

class InvoicePaid extends Notification
{
    // 省略

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return ((new InvoicePaidMail())->build())
            ->to($notifiable->email);
    }

    // 省略
}
App\Mail
    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->text('mail.invoice_paid');
    }

別にMailを挟まなくてもテンプレートって読み込めるんですかね・・・・?
よく分かりません(´・ω・`)

実装

メール送信時、いくつかのイベントが呼び出されていますが、そのうち送信前のイベントは NotificationSending です。
NotificationSending が呼ばれているのはここ。

Illuminate\Notifications\NotificationSender
    /**
     * Determines if the notification can be sent.
     *
     * @param  mixed  $notifiable
     * @param  mixed  $notification
     * @param  string  $channel
     * @return bool
     */
    protected function shouldSendNotification($notifiable, $notification, $channel)
    {
        return $this->events->until(
            new Events\NotificationSending($notifiable, $notification, $channel)
        ) !== false;
    }

「通知が送信可能かを判別」と、望んだままのイベントみたい。

NotificationSender にあるから、別にメール以外でも出来るんですかね?
そうだったら軽いタイトル詐欺になるのかしら・・・・(;´・ω・)

ともかく、このイベントをつかんでホワイトリストにかけるので、リスナーを登録。

App\Providers\EventServiceProvider
    protected $listen = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],
        'Illuminate\Notifications\Events\NotificationSending' => [
            'App\Listeners\CanSendMailListener'
        ]
    ];

あとは定義した CanSendMailListener を用意。

App\Listeners\CanSendMailListener
<?php

namespace App\Listeners;

use Illuminate\Container\Container;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Events\NotificationSending;
use Illuminate\Queue\InteractsWithQueue;
use App\Events\Event;

class CanSendMailListener
{
    /**
     * メールアドレスのホワイトリスト
     * @var array
     */
    const WHITE_DOMAINS = [
        '@example.com',
    ];

    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  Event  $event
     * @return void
     */
    public function handle(NotificationSending $event)
    {
        if (!\App::environment('staging')) {
            return;
        }

        $mailable = $event->notification->toMail($event->notifiable)->build();
        if (!$this->canSendToAddress($mailable->to[0]['address'])) {
            // 送信対象外なのでログに出力
            $mail_body = Container::getInstance()->make('mailer')->render(
                $mailable->textView, $mailable->buildViewData() ?? []
            );

            \Log::info('this email not exist white_list. not send.');
            \Log::info('to: '.json_encode($mailable->to));
            \Log::info('body: '.$mail_body);

            return false;
        }

        return true;
    }

    /**
     * 送信可能なメールアドレスか
     *
     * @access private
     * @param  string  $email
     * @return bool
     */
    private function canSendToAddress($email)
    {
        foreach (self::WHITE_DOMAINS as $domain) {
            if (strpos($email, $domain) !== false) {
                return true;
            }
        }
        return false;
    }
}

ステージング環境だけでこの処理をやりたいので、それ以外はスルーさせてます。

また、送信しない場合は、実際のメール本文の内容やらなんやらを確認したいかもなので、
ログへ出力するようにしています。
なお、今回はテキストメールだけだったので、メール本文の取得は決め打ちにしています。(´・ω・`)
HTMLメールの対応が必要な場合は、各自でもうちょいなんとかしてください(^_^;)
(もし自分でなんとかする案件があったら追記するかも。。。)

これを入れたうえで、実際にメール送信できるよう各種設定をいれれば完了。

あとがき

「ステージング環境はメールとばない、つらい('A`)」という共通認識がプロジェクトであったんですが、
とはいえ毎回ログを調べさせられるのもメンドいので、急きょ用意したのがコレ。
軽く確認して問題はでていないものの、いまだおっかなびっくりしてます(;´∀`)

あと、これ書いてて気がついたんですが、
判定自体の可否とか、ホワイトリストは .env とかに出して環境ごとに切り替えられるようにしてもいいかもしんないです。
もしかしたらステージング環境以外にお客さん公開用環境とか、審査用環境とか、似たようなのがあるかもしれないし・・・・。
まぁ、自分のときはなかったので、別にいっかなってσ(^_^;)

1
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
1
0