2021/10/25
この度SeciossLinkでプッシュ通知認証を対応することになりました。
プッシュ通知の実装方法を紹介させていただきます。
Android篇
Android へプッシュ通知送信する場合、Firebase を利用します。正式の名称はFirebase Cloud Messaging(FCM)と言います。
※ Googleのサービスですが、iOSでも利用できます。AndroidとiOSアプリ同時開発する時サーバー側の実装が共通できる、楽になると思います。
※ iOS使う場合、Firebaseの設定に APNs の登録が必要です、今回ブログは割愛します。
Android アプリを実装する場合、Android Studioで開発することはほとんどです。
今回 Android Studio 3.1 を使いました。
既存プロジェクトにプッシュ通知機能を追加する場合、(新規プロジェクトの場合普通のプロジェクト作成した後)
メニューバーの「Tool」を開き、「Firebase」を選んでいただければと思います。
そして、「Cloud Messaging」をクリック「Connected」しましょう。(Google のアカウントが必要になります。)
今回のプッシュ通知はログインで利用するのでユーザー個別で送信します、
端末を特定するため「topic」方式ではなく「token」方式にまります。
トークンの取得は、下記のCallback関数を追加いただけます。
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
String token = task.getResult();
Log.d("myTag", "myToekn: " + token);
}
});
トークンはプッシュ通知利用開始前のアカウント登録時にサーバーへ送信し、
サーバー側はユーザーと紐付けます。
サーバー側実装する前に 送信用API のキーなどの情報を取得します。
Android Studio で Firebase と連携できた後、Google 側自動的にプロジェクトが生成されます。
Google Cloud Platform へログインし、連携するプロジェクトを選び、「サービス アカウント」を開きます。
自動で生成されたサービスアカウントが確認できます。
名前は「firebase-adminsdk-xxx」のフォーマットで表示されると思います。(xxx はランダム文字列です)
「キー」タブに移動し、「新しい鍵を作成」で鍵を追加します。JSONのキータイプがお勧めです。
キーをダウンロードすると、下記の模様です。
{
"type": "service_account",
"project_id": "fir-pushexample-yyy",
"private_key_id": "74e6829f25778c8aba1390316453a060485a1310",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhk...省略...aB2sC2mr2A=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-xxx@fir-pushexample-yyy.iam.gserviceaccount.com",
"client_id": "12345678901234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-xxx%40fir-pushexample-yyy.iam.gserviceaccount.com"
}
続けて、サーバー側の実装ができるようになります(すみません、勝手に perl で実装しました)。
# Firebase Cloud Messaging 送信するため、認証用OAuth access_token を先に取得します。
# それから、FCMへプッシュ通知のリクエストを送信します。
# access_token 取得
my %header = ('alg' => 'RS256', 'typ' => 'JWT');
my %payload = (
'iss' => "firebase-adminsdk-mjmi7@fir-pushexample-6b08d.iam.gserviceaccount.com",
'scope' => "https://www.googleapis.com/auth/firebase.messaging",
'aud' => "https://oauth2.googleapis.com/token",
'exp' => time() + 3590,
'iat' => time(),
);
my $jwt = OIDC::Lite::Model::IDToken->new(
header => \%header,
payload => \%payload,
key => $key,
);
my $jwt_str = $jwt->get_token_string();
my %data_token = (
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt_str,
);
$req_token = POST($url_token, \%data_token);
$res_token = $ua->request($req_token);
my $content_token = decode_json($res_token->content);
my $access_token = $content_token->{'access_token'};
$access_token =~ s/\.+$//g;
# message 送信
my $data_msg = {
'message' => {
'token' => $token,
'data' => {
'sessid' => $sessid,
'username' => $username,
# location キーは予約されるキーワードで使えません
},
# notification キーが送信されると、dataが取得できなくなる
}
};
my %header_msg = (
'Content-Type' => 'application/json',
'Authorization' => "Bearer $access_token",
);
$req_msg = POST($url_msg, %header_msg, Content => encode_json($data_msg));
$res_msg = $ua->request($req_msg);
最後では Android 側受け取れ処理を実装すれば完成となります。
public class MyFcmService extends FirebaseMessagingService {
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// notification キーが送信されると、dataが取得できなくなる
RemoteMessage.Notification notification = remoteMessage.getNotification();
if (null != notification) {
Log.d("myTag", notification.toString());
}
// data キーの内容が受けられます
Map<String, String> data = remoteMessage.getData();
Log.d("myTag", "myMessage: " + data.toString());
long[] pattern = {0, 1000, 500, 1000};
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel("NOTIFICATION_CHANNEL_ID", "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription("");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(pattern);
notificationChannel.enableVibration(true);
notificationChannel.setBypassDnd(true);
manager.createNotificationChannel(notificationChannel);
// プッシュ通知をタッチする時起動する画面を定義します
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.putExtra("EXTRA_KEY_SESSID", sessid);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "NOTIFICATION_CHANNEL_ID");
notificationBuilder
.setContentTitle("プッシュ通知タイトル")
.setContentText("プッシュ通知内容")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setDefaults(Notification.DEFAULT_ALL)
.setColor(ContextCompat.getColor(this, R.color.purple_500))
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(resultPendingIntent)
.setAutoCancel(true);
manager.notify(1000, notificationBuilder.build());
}
}
AndroidManifest.xml に下記の定義追加必要になります。
<application
android:allowBackup="false"
android:icon="@drawable/ic_secioss"
android:label="@string/appname">
<service
android:name=".MyFcmService"
android:stopWithTask="false"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="MyPushNotification" />
実装はこれで以上になります。
プッシュ通知認証使うために、事前にAndroidアプリをダウンロードし、アカウントの登録を済ませて下さい。
登録方法は省略させていただきます。登録すると端末特定するためのトークンがサーバーに送信されます。
認証の流れとしては、
1 ユーザー プッシュ通知ログイン画面を開き、ユーザーIDを入力
2 サーバー ユーザーIDを基でで送信用トークンを取得
3 サーバー FCM送信用 access_token を取得
4 サーバー 認証用ヘッダー( access_token )と送信内容(セッションID)を添付し、トークン(Firebase)への送信リクエストを投げ、送信結果をブラウザまでに返す
5 ブラウザ 送信成功の結果が受けた後にサーバーへ認証可否のポーリングを始め
6 Android アプリ プッシュ通知が 端末までに届く、ユーザー許可を求む
7 ユーザーログイン許可をクリック
8 Android アプリ ログイン許可の結果とプッシュ通知の内容(セッションID)をサーバーへ送信
9 サーバー ログインを許可の結果をポーリング中のブラウザまでに返す
10 ブラウザ ポーリングの結果を基で、ログイン成功の画面に移す。
これで以上になります。