2014年4月1日 星期二

JavaMail IMAP Exchange Server 問題

stackoverflow 這裡有一篇跟我遇到同樣的問題,解法也一樣
http://stackoverflow.com/questions/10442877/java-mail-to-exchange-server-no-login-methods-supported

需求描述:
簡單來說,我需要跟 MS  Exchange Server 測試 IMAP 連線,並做後續 Mail 處理


問題 1. 最初的連線設定與 Exception:
TestIMAP.java
Properties props = new Properties();
session = Session.getDefaultInstance(props, null);
session.setDebug(true);
store = session.getStore("imap");
store.connect("host","user","password");


Exception:
DEBUG: setDebug: JavaMail version 1.4.5
DEBUG: getProvider() returning javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Sun Microsystems, Inc]
DEBUG: mail.imap.fetchsize: 16384
DEBUG: mail.imap.statuscachetimeout: 1000
DEBUG: mail.imap.appendbuffersize: -1
DEBUG: mail.imap.minidletime: 10
DEBUG: trying to connect to host "myHost", port 143, isSSL false
* OK The Microsoft Exchange IMAP4 service is ready.
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 LOGINDISABLED STARTTLS UIDPLUS CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG: protocolConnect login, host=myHost, user=myUser, password=
javax.mail.MessagingException: No login methods supported!;


根據此文章的答案,他也是參考自這裡
原因:
看這行 * CAPABILITY IMAP4 IMAP4rev1 LOGINDISABLED
Exchange Server 已回報 登入禁止

解決方法:
1. 升級JavaMail至1.4.5
2. 加上這行 props.put("mail.imap.starttls.enable", "true");


這樣就正常了嗎?? 並沒有~~


問題 2.:
Exception:
以上省略,跟問題1相同
A1 STARTTLS
A1 OK Begin TLS negotiation now.
DEBUG IMAP: STARTTLS Exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
messaging exception
javax.mail.MessagingException: STARTTLS failure;



這個問題就要參考這裡
原因:
我懶的翻譯了,就直接把重點貼出來吧~~XDD
MS Exchange SSL connection is not established properly by Java Mail API

解決方法:
其實我也沒很懂這個解法以及為什麼要這樣寫
猜測是去繼承覆寫掉 SSLSocketFactory 的 Method
在建構子寫一個空的信任管理物件去建立TLS SSLSocketFactory

public class ExchangeSSLSocketFactory extends SSLSocketFactory {

private SSLSocketFactory sslSocketFactory;
private SocketFactory socketFactory;

public ExchangeSSLSocketFactory() {
try {
socketFactory = SocketFactory.getDefault();

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { new EmptyTrustManager() },
null);
sslSocketFactory = (SSLSocketFactory) context.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private final class EmptyTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String authType)
throws CertificateException {
}

public void checkServerTrusted(X509Certificate[] cert, String authType)
throws CertificateException {
}

public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}

public static SocketFactory getDefault() {
return new ExchangeSSLSocketFactory();
}

@Override
public Socket createSocket(Socket socket, String s, int i, boolean flag)
throws IOException {
return sslSocketFactory.createSocket(socket, s, i, flag);
}

@Override
public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1,
int j) throws IOException {
return socketFactory.createSocket(inaddr, i, inaddr1, j);
}

@Override
public Socket createSocket(InetAddress inaddr, int i) throws IOException {
return socketFactory.createSocket(inaddr, i);
}

@Override
public Socket createSocket(String s, int i, InetAddress inaddr, int j)
throws IOException {
return socketFactory.createSocket(s, i, inaddr, j);
}

@Override
public Socket createSocket(String s, int i) throws IOException {
return socketFactory.createSocket(s, i);
}

@Override
public Socket createSocket() throws IOException {
return socketFactory.createSocket();
}

@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}

}

加上這兩個 Properties
props.setProperty("ssl.SocketFactory.provider", "your.package.name.ExchangeSSLSocketFactory");
props.setProperty("mail.imap.socketFactory.class", "your.package.name.ExchangeSSLSocketFactory");



OK, 打完收工

其實中間還有嘗試並看很多其他的解法,也有試著使用SSL imaps
但都無法成功,老實講 IMAP 的確比 POP3 麻煩與複雜多了
難怪現在還是一堆人只寫 POP3

其他參考資料
http://alvinalexander.com/blog/post/java/i-need-get-all-of-my-email-addresses-out-of-imap-mailbox
https://javamail.java.net/nonav/docs/api/

沒有留言:

張貼留言