Javaからウェブプッシュを簡単に送信する方法

こんにちは。 エキサイト株式会社の三浦です。

アプリにプッシュ通知があるように、ブラウザに対してもプッシュ通知を送ることができます。

これは「ウェブプッシュ」と呼ばれる仕組みで、ユーザにリーチするための手段の1つとなり得るものです。

今回は、Javaから簡単にこのウェブプッシュを送信する方法を紹介します。

ウェブプッシュとは

ウェブプッシュは、ブラウザに対してプッシュ通知を送る仕組みのことです。

ウェブプッシュによる通知

ウェブサイトを巡っていると、たまに「通知を許可しますか?」というポップアップが出てくるかと思いますが、あれがウェブプッシュの送信許可に使われており、許可するとウェブプッシュが送信されるようになります。

通知許可ポップアップ

賛否両論ある仕組みではありますが、適切に使用できれば、アプリのプッシュ通知と同じくユーザにリーチするための適切な手段になり得ます。

ウェブブッシュは、以下の流れで送られます。

  1. ユーザが特定のウェブサイトにアクセスすると、通知の許可を求められる
  2. 許可されると、ユーザのID等のデータが取得される
  3. そのID等を使って、サービス側からウェブプッシュを送信する
  4. ユーザのブラウザがウェブプッシュを受け取ると、通知として表示される

この流れの中では、細かく言うと「サービスワーカー」などの仕組みもあったりするのですが、今回は「3. そのID等を使って、サービス側からウェブプッシュを送信する」に注目していきます。

サービスからウェブプッシュを送信する方法

では具体的に、サービス側からウェブプッシュを送信するにはどうすればよいかを見ていきます。

サービス側からウェブプッシュを送信するには、最低限「ユーザのエンドポイント」が必要になります。 これはいわば、「ユーザのID」とも言えるものです。

このエンドポイントはユーザが通知を許可したタイミングで取得できるようになるもので、ブラウザとサービスの組み合わせごとに作られます。

そのため、例えば同じサイトでも別ブラウザであれば別のエンドポイントが作成されますし、同じブラウザでもサイトが異なれば別エンドポイントが作成されます。

そして、そのエンドポイントを使うことにより、ユーザにウェブプッシュを送ることができるのです。

ただし、実はエンドポイントだけでは、「ウェブプッシュを送ること」しかできません。

通常ウェブプッシュを送る場合、以下のことをしたいはずです。

  • タイトルや本文を付け、どんな通知かわかるようにする
  • 通知をクリックすると、指定のページに飛ぶようにする

ウェブプッシュによる通知

ですが、エンドポイントだけでは「ウェブプッシュを送ること」はできても「ウェブプッシュにメッセージを含めて送ること」はできないので、上記のようなことを実現できません。

そこで必要になってくるのが、「ユーザの公開鍵」と「ユーザの認証シークレット」です。

これらもユーザが通知を許可したタイミングで取得できるようになるのですが、これらを使うことでウェブプッシュにメッセージを含めることができます。

ただ、これらを使ってウェブプッシュにメッセージを含めるためには、複雑な暗号化処理が必要になってきます。

自前で実装することはもちろん可能ではありますが、かなりの手間になってしまいます。

web.dev

これを、簡単に送る方法はないのでしょうか?

サービスからウェブプッシュを簡単に送信する方法

実は、このあたりをまとめてくれているライブラリが存在します。

github.com

使い方はとても簡単です。 GradleとSpring Bootを使っている場合は、以下の手順で使用可能です。

// webpushライブラリ本体と、ライブラリ内で使うセキュリティ用ライブラリを指定

dependencies {
    implementation "nl.martijndwars:web-push:5.1.1"
    implementation "org.bouncycastle:bcprov-jdk18on:1.72"
}
// 送信設定をDIする。送信前に毎回定義しても問題ないが、DIしたほうが使いやすい

@Configuration
@RequiredArgsConstructor
public class WebPushConfig {
    private final WebPushProperties webPushProperties;

    /**
     * WebPush設定用プロパティ
     * ここでは application.yml に定義したものを取得することを想定しているが、どんな方法でも問題ない
     *
     * @param fcmApiKey 送信用FCM APIキー
     */
    @ConfigurationProperties(prefix = "sample")
    public record WebPushProperties(String fcmApiKey) {}

    /**
     * PushServiceのBean化
     * @return 設定済みPushService
     */
    @Bean
    public PushService pushService() {
        Security.addProvider(new BouncyCastleProvider());
        return new PushService(this.webPushProperties.fcmApiKey());
    }
}
# application.yml

sample:
  fcm_api_key: "*****"
// 実際にウェププッシュを送信

@RequiredArgsConstructor
public class Push {
    private final PushService pushService;

    public void sendPush(String endpoint, String publicKey, String authenticationSecret, byte[] payload) {
        try {
            Notification notification = new Notification(endpoint, publicKey, authenticationSecret, payload);

            HttpResponse response = pushService.send(notification);
        } catch (Exception exception) {
            log.error(exception.getMessage());
        }
    }
}

これで、メッセージ付きでウェブプッシュを送信することができます。

Java17でも正しく動くことを確認済みです。

なおユーザ数が多くなると、1件ずつ送信していると大変な時間がかかってしまう可能性があるので、適切な並列処理・非同期処理などを利用しましょう。

最後に

ウェブプッシュは、裏側でかなり複雑な暗号化処理を行う仕組みです。

可能な限りよしなにやってくれるライブラリを利用して、我々はビジネスロジックに注力していきましょう!