このブログエンジンblog-java2では、新しいコメントが投稿されると、私のGmailアドレス宛にメールが送信されるようになっています。
今までこのメール送信にはSTARTTLSを使っていたのですが、生のパスワードが必要なため、Googleアカウントの「安全性の低いアプリの許可」を有効にする必要がありました。
またこれを許可したとしても、例えばVPSなど、普段と異なるIPアドレス帯から認証しようとすると、アクセスがブロックされることがありました。
そもそも、(難読化されているとはいえ)サーバにGoogleアカウントの生パスワードが保存されているのは宜しくありません。
Gmail APIによるOAuth2.0認証に移行することで、この問題を解決します。基本的には公式の手順に従うだけなのですが、一般的なOAuthアプリケーションと異なり特定のアカウントからメール送信するだけなので、もっとシンプルになります。
以下の準備が必要になります。
このJSONに、クライアントIDやクライアントシークレットなどが含まれています。client_secret.jsonとしてローカルに保存しておきます。
情報にアクセスするためにはアクセストークンが必要ですが、セキュリティの制約上、有効期限が短く設定されています。初回認証時に取得できるリフレッシュトークンを使って、必要に応じてアクセストークンを更新しなければなりません。
Gmail APIとclient_secret.jsonを使うと、上記のような面倒さを隠蔽してくれます。
https://developers.google.com/gmail/api/quickstart/java
上記の公式サンプルからエッセンスを抽出し、SmtpManager.javaを作成しました。
private static Gmail getGmailService(String basedir, String appName) throws Exception {
// 機密情報ファイルのパス
File DATA_STORE_DIR = new java.io.File(basedir, "gmail-secrets");
File SECRET_JSON = new java.io.File(DATA_STORE_DIR, "client_secret.json");
// 準備
FileDataStoreFactory DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
HttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
// 送信のみ
List<String> SCOPES = Arrays.asList(GmailScopes.GMAIL_SEND);
// Credential取得
try (InputStream in = FileUtils.openInputStream(SECRET_JSON)) {
GoogleClientSecrets clientSecrets
= GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow
= new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
JSON_FACTORY, clientSecrets, SCOPES).setDataStoreFactory(
DATA_STORE_FACTORY).setAccessType("offline").build();
Credential credential = new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
// Gmailインスタンス生成
return new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY,
credential).setApplicationName(appName).build();
}
}
public static void main(String[] args) throws Throwable {
Gmail gmail = getGmailService("target", "test");
System.out.println(gmail.toString());
}
以下の手順により、アプリで使用する認証情報を取得できます。
この2ファイルを/home/username/gmail-secrets/にコピーすれば、アプリでは以下のコードでメール送信できます。
Gmail service = getGmailService(System.getProperty("user.home"), title);
service.users().messages().send("me", gmailmsg).execute();
繰り返しになりますが、上記は特定のアカウントだけにアクセスする場合の手順です。
一般のOAuthアプリであれば、ユーザ毎にリフレッシュトークンの保存が必要になるはずです。