我有一个使用AccountManager存储用户帐户的应用程序.用户使用OAuth2.0 password-username凭据流登录并通过我的REST API注册.
用户收到的访问令牌将在2小时后到期,需要刷新才能再次过期,依此类推.
我需要在我的身份验证器中实现这个刷新功能.
我有一个模型AccessToken
,其中包含以下字段:
String accessToken, String tokenType, Long expiresIn, String refreshToken, String scope, Long createdAt
.
所以目前,我getAuthToken
在AccountAuthenticator
课堂上的方法中收到了这个AccessToken
对象,并将其accessToken
字段用作Auth Token
我的客户经理.
我需要的是以某种方式使用我的客户经理存储刷新令牌和身份验证令牌,当应用程序尝试访问API并获得错误响应时:{"error": "access token expired"}
,使用先前收到refreshToken
的AccessToken
对象中的字符串刷新当前访问令牌.但是,我不确定应该怎么做.
我getAuthToken
在authenticator类中的方法目前如下所示:
@Override public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String authTokenType, Bundle options) throws NetworkErrorException { if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) { final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ERROR_MESSAGE, "Invalid authTokenType"); return result; } final AccountManager manager = AccountManager.get(context.getApplicationContext()); String authToken = manager.peekAuthToken(account, authTokenType); Log.d("Discounty", TAG + " > peekAuthToken returned - " + authToken); if (TextUtils.isEmpty(authToken)) { final String password = manager.getPassword(account); if (password != null) { try { authToken = discountyService.getAccessToken(DiscountyService.ACCESS_GRANT_TYPE, account.name, password).toBlocking().first().getAccessToken(); // ======= // Here the above discountyService.getAccessToken(...) call returns // AccessToken object on which I call the .getAccessToken() // getter which returns a string. // ======= } catch (Exception e) { e.printStackTrace(); } } } if (!TextUtils.isEmpty(authToken)) { final Bundle result = new Bundle(); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); result.putString(AccountManager.KEY_AUTHTOKEN, authToken); return result; } final Intent intent = new Intent(context, LoginActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse); intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type); intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; }
并且该类AccountGeneral
只包含一些常量:
public class AccountGeneral { public static final String ACCOUNT_TYPE = "com.discounty"; public static final String ACCOUNT_NAME = "Discounty"; public static final String AUTHTOKEN_TYPE_READ_ONLY = "Read only"; public static final String AUTHTOKE_TYPE_READ_ONLY_LABEL = "Read only access to a Discounty account"; public static final String AUTHTOKEN_TYPE_FULL_ACCESS = "Full access"; public static final String AUTHTOKEN_TYPE_FULL_ACCESS_LABEL = "Full access to a Discounty account"; }
该应用程序也将使用SyncAdapter
并将经常与API交互以同步来自服务器的数据,并且这些API调用还需要使用访问令牌作为请求中的参数,因此我真的需要实现此刷新功能并使其自动化.
有人知道如何正确实现吗?
PS:我将使用本地数据库来存储我的所有数据,我也可以存储令牌对象.这似乎是一个容易入侵,虽然不安全.也许我应该一次只存储一个刷新令牌作为db记录,并在应用程序收到新令牌时更新它?
PPS:无论如何我都可以自由地改变API的工作方式,所以如果有关于通过改进API来改进移动应用程序的建议,那么他们也非常感激.
您可以在首次使用以下帐户添加帐户时将刷新令牌保存到帐户的用户数据中:
Bundle userdata = new Bundle; userdata.putString("refreshToken", refreshToken); mAccountManager.addAccountExplicitly (account, password, userdata);
您还可以在添加帐户后通过调用以下方式设置userdata:
mAccountManager.setUserData(account, "refreshToken", refreshToken);
当您访问令牌过期时,您可以通过调用以下方式检索刷新令牌:
String refreshToken = mAccountManager.getUserData(account, "refreshToken");
使用refreshToken检索新的访问令牌.