« Java:ログインパスワードを暗号化する | トップページ | Java:MySQLに金額を格納する »

JavaMail:メール送信でOP25B対応

【概要】
昨今のスパムメール対策により,Javaプログラムからのメール送信が困難な場合がでてきています。送信先がLAN内だったり,LAN内のメールサーバ(MTA)をリレーしたり,ISPが提供するメールサーバをリレーしてメール送信するなら問題ありませんが,JavaプログラムからISPのユーザ回線を経由してインターネットの向こう側に設置しているメールサーバでメールをリレーしようとすると,最近各ISPが導入を進めているOutbound Port 25 Blocking(OP25B)によりメール送信が阻止されてしまうことがあります。
Outbound Port 25 Blockingとは,一般ユーザのパソコンから送信用メールサーバをリレーせずに直接送信先のメールサーバへメールを送りつけるのを禁止するもので,ISPのユーザ回線から外部の25番ポート(SMTP)へ接続するのを阻止することで実現しています。これにより25番ポートからインターネットへメール送信できるのはISPのメールサーバや固定IPを取得したホストに限られますが,メールをリレーする送信用メールサーバがISPの外側にいる場合にも接続が阻止される弊害が出ています。
この対策としては,ISPの外側にいるメールサーバがメール送信のリレーを受け付けるときは25番以外のポート番号を使用すればよいということになります。この代替ポートとして「サブミッションポート」と呼ばれる587番ポートを使うようメールサーバを設定することができます。このとき587番ポートが正規のアカウントによるメール送信のみに使われるように,587番ポートはSASL認証必須に設定します。メールサーバ側のサブミッションポート対応についてはこちらの記事の後半にて,Mac OS X Serverで稼動するPostfixの設定方法を解説しています。

【JavaMailによる実装例】
この記事では,JavaプログラムからJavaMailを使ってメール送信するときに,メールサーバに25番ではなく587番ポートに接続して,SASL認証を行った上でメール送信するコードを例示します。なお,JavaMailを使用するには,事前にJavaMailとJAFをダウンロードして,クラスパスで指定されているディレクトリにJavaMailの「mail.jar」とJAFの「activation.jar」をインストールしておく必要があります。(JAFはJava SE 6に含まれるようになったので,Java SE 6ではJAFのインストールは不要です。)
JavaMailダウンロード:[JavaMail][JAF]
クラスパスの設定:[概要][Windows][Solaris][Mac OS X]
下記のコードではSASLの認証方法として「AUTH LOGIN」で認証できることを確認しています。認証方法「CRAM-MD5」については,JavaMailではSMTPで未サポートのようです(出典)。

下記のコードは25番ポートへ認証なしにメールを送信する場合と以下の点が異なります。
1)接続先SMTPサーバのポート番号を587番に指定:
→プロパティ「mail.smtp.port」を「587」に設定。

2)SASL認証(SMTP Auth)を試みるよう指定:
→プロパティ「mail.smtp.auth」を「true」に設定。

3)SASL認証で使用する認証情報クラスを用意する:
→Authenticatorクラスを継承し,getPasswordAuthenticationメソッドを実装したクラス(PassAuther)を用意する。

4)Session取得時に認証情報クラスインスタンスで初期化:
→Session.getDefaultInstance()メソッドの第二引数にPassAutherのインスタンスを渡す。

【WebObjectsでJavaMailを使う場合の注意】
WebObjectsアプリケーションではデフォルトで「Session」というクラスが作られるため,JavaMailのSessionクラスとクラス名のバッティングが起こります。そのためJavaMailのSessionクラスを指定する場合に「javax.mail.Session」とフルパスでクラス名を指定するなどの対応が必要です。

JavaMailを使って587番ポートからSASL認証してメール送信を行う実装例:

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
/**
 * SendSASLMail:メール送信クラス.
 */
public class SendSASLMail
{
  /**
   * PassAuther:認証情報クラス.
   */
  private class PassAuther extends Authenticator
  {
    private String mUserName;
    private String mPassword;
    //------------------------------------------------------------------
    /**
     * PassAutherクラスのコンストラクタ.
     * @param userName SASL認証で使うユーザ名.
     * @param passwd  同パスワード.
     */
    public PassAuther (
      String userName,
      String passwd)
    {
      super();
      mUserName = userName;
      mPassword = passwd;
    }
    //------------------------------------------------------------------
    /**
     * Authenticatorの派生クラスで必須のメソッド.
     */
    public PasswordAuthentication getPasswordAuthentication ()
    {
      return new PasswordAuthentication(mUserName, mPassword);
    }
  }

  private String mHostName;
  private String mUserName;
  private String mPassword;

  //------------------------------------------------------------------
  /**
   * SendSASLMailクラスのコンストラクタ.
   * @param host   送信用ホスト名.
   * @param userName SASL認証用ユーザ名.
   * @param passwd  SASL認証用パスワード.
   */
  public SendSASLMail (
    String host,
    String userName,
    String passwd)
  {
    mHostName = host;
    mUserName = userName;
    mPassword = passwd;
  }

  //------------------------------------------------------------------
  /**
   * メール送信実行メソッド.
   * @param from   差出人.
   * @param toList  宛先リスト.
   * @param ccList  CCリスト.
   * @param bccList BCCリスト.
   * @param subject Subject.
   * @param message メール本文.
   * @throws MessagingException
   * @throws AuthenticationFailedException
   * @throws SendFailedException
   */
  public void send (
    Address  from,
    Address[] toList,
    Address[] ccList,
    Address[] bccList,
    String   subject,
    String   message)
    throws MessagingException, AuthenticationFailedException, SendFailedException
  {
    if (from != null && toList != null && 0 < toList.length) {
      Properties props = new Properties();
      props.setProperty("mail.transport.protocol", "smtp");
      props.setProperty("mail.smtp.host", mHostName);
      props.setProperty("mail.smtp.port", "587");
      props.setProperty("mail.host", mHostName);
      props.setProperty("mail.smtp.connectiontimeout", "60000");
      props.setProperty("mail.smtp.timeout", "60000");
      props.setProperty("mail.smtp.auth", "true");

      Authenticator auther = new PassAuther(mUserName, mPassword);
      Session session = Session.getDefaultInstance(props, auther);

      // session.setDebug(true); // デバッグ情報を表示する.
      MimeMessage mmsg = new MimeMessage(session);

      mmsg.setFrom(from);
      mmsg.setRecipients(Message.RecipientType.TO, toList);
      if (ccList != null && 0 < ccList.length) {
        mmsg.setRecipients(Message.RecipientType.CC, ccList);
      }
      if (bccList != null && 0 < bccList.length) {
        mmsg.setRecipients(Message.RecipientType.BCC, bccList);
      }
      if (subject != null && 0 < subject.length()) {
        mmsg.setSubject(subject, "iso-2022-jp");
      }
      mmsg.setText(message, "iso-2022-jp");
      mmsg.setSentDate(new Date());

      Transport.send(mmsg);
    }
  }

  //------------------------------------------------------------------
  /**
   * 動作確認用mainメソッド.
   * @param args
   */
  public static void main(String[] args)
  {
    SendSASLMail sender = new SendSASLMail("mail.example.com", "username", "password");
    try {
      Address from = new InternetAddress("address@example.com", "差出人", "iso-2022-jp");
      Address to = new InternetAddress("addr1@example.com", "宛先1(TO)", "iso-2022-jp");
      Address cc = new InternetAddress("addr2@example.com", "宛先2(CC)", "iso-2022-jp");

      Address[] toList = new Address[] {to};
      Address[] ccList = new Address[] {cc};

      String subject = "テストメール";
      String message = "JavaMailからのメールです。";

      sender.send(from, toList, ccList, null, subject, message);
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

※上記コードでは,整形のため全角スペースを使用している部分があります。
【著作権表記】上記コードを含む本ブログのプログラムコードは,私的利用可,商用利用可,改変しての利用可です。利用の際に作者に許諾を得る必要はありません。

■関連情報
ISPによるOP25B 実施状況((財)日本データ通信協会・迷惑メール相談センター)
メール送信サンプル:[JavaMail(SMTP)編(JavaでHelloWorld)][JavaMailでメール送信アプリを作る(@IT Java Tips)]
JavaMail完全解説
Sun:[JavaMail]
API Reference(javadoc):[JavaMail][J2EE 1.3.1]
ダウンロード:[JavaMail][JAF]
WebObjects:WOMailDeliveryで日本語メール送信

■関連書籍をAmazonで検索:[JavaMail][Java]
独習Java サーバサイド編 第2版 (kindle版)



にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。

|

« Java:ログインパスワードを暗号化する | トップページ | Java:MySQLに金額を格納する »

Java」カテゴリの記事

プログラミング」カテゴリの記事

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/505963/40308479

この記事へのトラックバック一覧です: JavaMail:メール送信でOP25B対応:

« Java:ログインパスワードを暗号化する | トップページ | Java:MySQLに金額を格納する »