Android OAuth2 Bearer令牌最佳做法

nek*_*jsi 9 android accountmanager oauth-2.0 android-syncadapter android-volley

这个很好的教程非常好地介绍了Android上的帐户身份验证,并通过使用Android来实现AccountManager.

但是,我需要使用承载令牌为OAuth2 API创建客户端应用程序以进行身份​​验证.在获得令牌时,我收到了它的到期时间戳,但我不清楚存储的位置以及如何正确使用它.问题是,如果我不想不必要地去服务器,应用程序会意识到,只有在请求任何随机资源时从服务器收到HTTP 401错误后,承载才会变为无效.那么,解决这个问题的最佳做法是什么:

  1. 我的代码中的每个网络请求都应该具有重试机制,以防承载令牌在此期间变为无效吗?我可能会invalidateAuthToken在捕获异常时重试.
  2. 可以同步适配器以某种方式帮助吗?

由于我是Android开发的新手,我希望解决方案也可能与我预期的完全不同.

如果它是相关的,我打算使用Volley进行服务器通信.

nek*_*jsi 9

经过一番调查后我发现了自己的答案:

  1. 是的,调用AccountManager#invalidateAuthToken将删除上次保存的身份验证令牌(OAuth2案例中的访问令牌),并期望您在下次AccountAuthenticator#getAuthToken调用时检测到该令牌.例如,以下是该方法的代码:

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        // Extract the username and password from the Account Manager, and ask
        // for an appropriate AuthToken.
        final AccountManager am = AccountManager.get(mContext);
    
        String authToken = am.peekAuthToken(account, authTokenType);
    
        // EXTRA: I am also storing the OAuth2 refresh token in the AccountManager
        Map<String, String> refreshResult = null;
        String refreshToken = am.getUserData(account, KEY_REFRESH_TOKEN);
        if (TextUtils.isEmpty(authToken) && !TextUtils.isEmpty(refreshToken)) {
            // lets try to refresh the token
            // EXTRA: AuthenticationProvider is my class for accessing the authentication server, getting new access and refresh token based on the existing refresh token
            refreshResult = AuthenticationProvider.
                refreshAccessToken(am.getUserData(account, KEY_REFRESH_TOKEN));
        }
    
        // If we get a result from the refresh - we return it
        if (!refreshResult.isEmpty()) {
            authToken = refreshResult.get(AccountManager.KEY_AUTHTOKEN);
            // EXTRA: new refresh token used only in OAuth2
            refreshToken = refreshResult.get(KEY_REFRESH_TOKEN);
    
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
    
            // store the new tokens in the system
            am.setAuthToken(account, authTokenType, authToken);
            am.setUserData(account, KEY_REFRESH_TOKEN, refreshToken);
    
            result.putString(AccountManager.KEY_AUTHTOKEN, refreshResult.get(AccountManager.KEY_AUTHTOKEN));
            result.putString(KEY_REFRESH_TOKEN, refreshResult.get(KEY_REFRESH_TOKEN));
            return result;
        }
    
        // If we get here, then we couldn't access the user's password - so we
        // need to re-prompt them for their credentials. We do that by creating
        // an intent to display our AuthenticatorActivity.
        final Intent intent = new Intent(mContext, LoginActivity.class);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
        final Bundle bundle = new Bundle();
        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
        return bundle;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我还收到了问题中提到的博客文章的作者的确认.

  2. SyncAdapters无法直接帮助,因为它们的真正目的是异步(对于开发人员)和透明地(对于用户)从网络获取数据.他们只是AbstractAccountAuthenticator在适当的时候使用并调用它的方法.