【发布时间】:2014-10-15 06:24:52
【问题描述】:
我的 Android 应用必须通过具有 OAuth2 的 IMAP 访问 Gmail,无需用户密码。 我已经实现了这个例子: https://java.net/projects/javamail/pages/OAuth2 根据需要添加提供程序(SASL XOAUTH2 提供程序将在首次使用 SASL 支持时添加到 Java 安全配置中)
这是我的代码:
-
获取访问令牌
public void getAndUseAuthTokenBlocking() throws JSONException { String token = ""; try { // Retrieve a token for the given account and scope. It will always return either // a non-empty String or throw an exception. String scope = "oauth2:https://mail.google.com/"; Log.i("Access TOKEN account",accountName); token = GoogleAuthUtil.getToken(activity.getApplicationContext(), accountName, scope); // Do work with token. URL url = new URL("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="+ token); HttpURLConnection con = (HttpURLConnection) url.openConnection(); int serverCode = con.getResponseCode(); if (serverCode == 200) { access_token = token; return; //bad token, invalidate and get a new one } else if (serverCode == 401) { // invalidate the token that we found is bad so that GoogleAuthUtil won't // return it next time (it may have cached it) GoogleAuthUtil.invalidateToken(activity.getApplicationContext(), token); // consider retrying getAndUseTokenBlocking() once more Log.w("Access TOKEN","INVALIDATO"); getAndUseAuthTokenBlocking(); } else { Log.e("SERVER CODE","Server returned the following error code: " + serverCode); return; } return; } catch (GooglePlayServicesAvailabilityException playEx) { Dialog alert = GooglePlayServicesUtil.getErrorDialog( playEx.getConnectionStatusCode(), activity,1); } catch (UserRecoverableAuthException userAuthEx) { // Start the user recoverable action using the intent returned by // getIntent() activity.startActivityForResult( userAuthEx.getIntent(), 1); return; } catch (IOException ioEx) { // network or server error, the call is expected to succeed if you try again later. // Don't attempt to call again immediately - the request is likely to // fail, you'll hit quotas or back-off. Log.e("IOException", "transient error encountered: " + ioEx.getMessage()); return; } catch (GoogleAuthException authEx) { // Failure. The call is not expected to ever succeed so it should not be // retried. Log.e("GoogleAuthException", "Unrecoverable authentication exception: " + authEx.getMessage(), authEx); return; } } -
提供者的创建(OAuthFactory e OAuthClient 类取自https://code.google.com/p/google-mail-oauth2-tools/source/browse/trunk/java/#java%2Fcom%2Fgoogle%2Fcode%2Fsamples%2Foauth2)
public static final class OAuthProvider extends Provider { private static final long serialVersionUID = 1L; public OAuthProvider() { super("Google OAuth2 Provider", 1.0, "Provides the XOAUTH2 SASL Mechanism"); String className = OAuthFactory.class.getName(); put("SaslClientFactory.XOAUTH2", className); } } -
IMAP 连接
protected String doInBackground(String... params) { Log.i("account",accountName); try { getAndUseAuthTokenBlocking(); } catch (JSONException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Log.i("access token",access_token); initialize(); Properties props = new Properties(); props.put("mail.imap.ssl.enable", "true"); // required for Gmail props.put("mail.imap.sasl.enable", "true"); props.put("mail.imap.sasl.mechanisms", "XOAUTH2"); props.put("mail.imap.auth.login.disable", "true"); props.put("mail.imap.auth.plain.disable", "true"); Session session = Session.getInstance(props); session.setDebug(true); Store store; try { store = session.getStore("imaps"); store.connect("imap.gmail.com", accountName, access_token); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
但是这段代码给出了 [AUTHENTICATIONFAILED] Invalid credentials (Failure) 错误
08-21 16:14:43.100: I/System.out(7346): DEBUG: setDebug: JavaMail version 1.5.2
08-21 16:14:43.100: I/System.out(7346): DEBUG: getProvider() returning javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle,1.5.2]
08-21 16:14:43.100: W/dalvikvm(7346): method Lcom/sun/mail/imap/IMAPStore;.getSession incorrectly overrides package-private method with same name in Ljavax/mail/Service;
08-21 16:14:43.104: I/System.out(7346): DEBUG IMAPS: mail.imap.fetchsize: 16384
08-21 16:14:43.104: I/System.out(7346): DEBUG IMAPS: mail.imap.ignorebodystructuresize: false
08-21 16:14:43.104: I/System.out(7346): DEBUG IMAPS: mail.imap.statuscachetimeout: 1000
08-21 16:14:43.104: I/System.out(7346): DEBUG IMAPS: mail.imap.appendbuffersize: -1
08-21 16:14:43.104: I/System.out(7346): DEBUG IMAPS: mail.imap.minidletime: 10
08-21 16:14:43.108: I/System.out(7346): DEBUG IMAPS: trying to connect to host "imap.gmail.com", port 993, isSSL true
08-21 16:14:48.408: I/System.out(7346): * OK Gimap ready for requests from 82.57.18.250 z47mb29911677eep
08-21 16:14:48.408: I/System.out(7346): A0 CAPABILITY
08-21 16:14:48.468: I/System.out(7346): * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH AUTH=XOAUTH2 AUTH=PLAIN AUTH=PLAIN-CLIENTTOKEN
08-21 16:14:48.468: I/System.out(7346): A0 OK Thats all she wrote! z47mb29911677eep
08-21 16:14:48.468: I/System.out(7346): DEBUG IMAPS: AUTH: XOAUTH
08-21 16:14:48.468: I/System.out(7346): DEBUG IMAPS: AUTH: XOAUTH2
08-21 16:14:48.468: I/System.out(7346): DEBUG IMAPS: AUTH: PLAIN
08-21 16:14:48.468: I/System.out(7346): DEBUG IMAPS: AUTH: PLAIN-CLIENTTOKEN
08-21 16:14:48.472: I/System.out(7346): DEBUG IMAPS: protocolConnect login, host=imap.gmail.com, user=myemail@gmail.com, password=<non-null>
08-21 16:14:48.472: I/System.out(7346): DEBUG IMAPS: AUTHENTICATE PLAIN command trace suppressed
08-21 16:14:49.488: I/System.out(7346): DEBUG IMAPS: AUTHENTICATE PLAIN command result: A1 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)
08-21 16:14:49.488: W/System.err(7346): javax.mail.AuthenticationFailedException: [AUTHENTICATIONFAILED] Invalid credentials (Failure)
08-21 16:14:49.492: W/System.err(7346): at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:715)
08-21 16:14:49.492: W/System.err(7346): at javax.mail.Service.connect(Service.java:364)
08-21 16:14:49.492: W/System.err(7346): at javax.mail.Service.connect(Service.java:245)
08-21 16:14:49.492: W/System.err(7346): at com.example.tickets.Reader2.doInBackground(Reader2.java:113)
08-21 16:14:49.492: W/System.err(7346): at com.example.tickets.Reader2.doInBackground(Reader2.java:1)
08-21 16:14:49.492: W/System.err(7346): at android.os.AsyncTask$2.call(AsyncTask.java:287)
08-21 16:14:49.492: W/System.err(7346): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
08-21 16:14:49.492: W/System.err(7346): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
08-21 16:14:49.492: W/System.err(7346): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
08-21 16:14:49.500: W/System.err(7346): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
08-21 16:14:49.500: W/System.err(7346): at java.lang.Thread.run(Thread.java:841)
我不明白问题是 IMAP 连接还是访问令牌;为了获取访问令牌,我也尝试了 oauth.py 但结果是一样的。
感谢您的回复。
【问题讨论】:
-
您的日志显示您正在发出 AUTHENTICATE PLAIN 命令,而服务器正在发出带有 AUTHENTICATIONFAILED 响应代码的 NO 响应。所以你想使用 Oauth2,但实际上你使用的是 Plain。
-
根据 javamail 文档,我使用了 props.put("mail.imap.auth.plain.disable", true"); mail.imap.auth.plain.disable boolean If true , 阻止使用 AUTHENTICATE PLAIN 命令。默认为 false。
-
您正在为“imap”协议设置一堆属性,但您使用的是“imap”协议。将 getStore 调用更改为使用“imap”。此外,如果您使用的是内置 OAuth 支持的最新 JavaMail 版本,则不需要所有 Google OAuth 代码。
-
感谢 Bill 的回复,问题是 getStore 方法中的协议名称。但现在我对 SASL 身份验证有一些问题:08-21 21:46:39.102: I/System.out(2234): DEBUG IMAPS: Can't load SASL authenticationator, THROW: 08-21 21:46:39.102: I/System.out(2234): java.lang.ClassNotFoundException: com.sun.mail.imap.protocol.IMAPSaslAuthenticator 我试过使用mannaz.at/codebase/imap-ssl-mail-android,但我总是遇到 sasl 身份验证问题。 “您不需要所有的 Google OAuth 代码”是什么意思?我使用 javamail 1.5.2 release.Tnx
-
如果有人对此有解决方案,请在此处发布。
标签: java android oauth-2.0 jakarta-mail imap