我想向我的应用的用户展示一系列要使用的帐户(启用共享设备,例如一个部门,但仍然能够识别用户)。 用户标识和授权通过AWS Cognito用户池工作 - >联合身份 - > STS。
我的问题是,我似乎无法让AWS SDK与Android帐户管理器(我也想与SyncManager一起使用)很好地集成。
所以这是我目前的做法:
我试图将用户身份验证与api调用分开。用户身份验证应该通过AccountManager进行,一旦提供了AuthToken,就会配置CloudService(绑定到MainActivity)中的credentialsProvider,并且可以进行api调用。为了更多地使用AWS Cognito SDK,我创建了一个单例来保存UserPool,当前用户和会话,但没有真正的效果。
到目前为止,这个解决方案给我的是,我可以登录并使用该应用程序一段时间。 1小时后,我存储在AccountManager中的访问令牌已过期,我无法直接刷新它。我能够刷新令牌的唯一方法是不使用AccountManager,因为AWS CachedCredentialsProvider负责令牌刷新。但是,这是基于使用sharedPreferences而我无法看到当前缓存的用户是谁,因此不适合使用它。
长话短说,我试图将所有重要代码放在下面。我希望你能指出我正确的方向。可能我已经远离建筑...... 提前谢谢!
AccountSelectionActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account_selection);
List<Account> availableAccounts = new ArrayList<>();
mAdapter = new AccountsViewAdapter(availableAccounts, this);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rvAccountsList);
if (null != recyclerView) {
recyclerView.setLayoutManager(
new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(mAdapter);
} else {
Log.e(TAG, "no RecyclerView found");
}
accountManager = AccountManager.get(getApplicationContext());
loadAvailableAccounts();
}
void loadAvailableAccounts(){
Account[] accounts = accountManager.getAccountsByType(
getString(R.string.account_type));
if (0 == accounts.length) {
addAccount(null);
} else {
mAdapter.setAccountsList(accounts);
}
}
// called upon account selection
public void onClick(Account account) {
selectedAccount = account;
Intent intent = new Intent(this, MainActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_ACCOUNT_NAME, selectedAccount);
intent.putExtras(bundle);
startActivity(intent);
finish();
}
MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
mAccount = bundle.getParcelable(AccountManager.KEY_ACCOUNT_NAME);
if (mAccount == null) {
finishDueToMissingAccount();
}
} else {
finishDueToMissingAccount();
}
setContentView(R.layout.activity_buddy);
[...]
}
// called from base class after binding the CloudService
void onCloudServiceConnected() {
if (mAccount != null ) {
cloudService.logIn(mAccount, this);
} else {
Log.e(TAG, "mAccount = " + mAccount.toString());
}
}
CloudService
@Override
public void onCreate() {
accountManager = AccountManager.get(getApplicationContext());
credentialsProvider = new CognitoCachingCredentialsProvider(
this, // context
IDENTITY_POOL_ID,
IDENTITY_POOL_REGION);
syncManager = new CognitoSyncManager(
this, // context
IDENTITY_POOL_REGION,
credentialsProvider);
}
[...]
public void logIn(Account account, Activity activity) {
this.account = account;
accountManager.getAuthToken(account,
"n/a", // token type
null, // options
activity,
new OnTokenAcquired(), // callback
null // handler
);
}
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
@Override
public void run(AccountManagerFuture<Bundle> result) {
try {
Map<String, String> logins = new HashMap<>();
String token = result.getResult().getString(
AccountManager.KEY_AUTHTOKEN);
logins.put(USER_POOL_ARN, token);
credentialsProvider.setLogins(logins);
} catch (IOException e) {
// TODO: Handle Exception
} catch (OperationCanceledException e) {
// TODO: Handle Operation Canceled
} catch (AuthenticatorException e) {
// TODO: Hanlde Authenticator exception
}
}
}
身份验证
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures,
Bundle options) throws NetworkErrorException {
return promptToLogin(response, options);
}
private Bundle promptToLogin(AccountAuthenticatorResponse response, Bundle options) {
options = (options == null) ? new Bundle() : options;
final Intent intent = new Intent(context, AuthenticatorActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtras(options);
final Bundle result = new Bundle();
result.putParcelable(AccountManager.KEY_INTENT, intent);
return result;
}
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
Bundle result = new Bundle();
if (accountExists(account.type)) {
String token = accountManager.peekAuthToken(account, authTokenType);
if ( token != null ) {
return passBackCurrentAuthToken(account.name, authTokenType,
result, token);
}
}
return promptToLogin(response, options);
}
private Bundle passBackCurrentAuthToken(String accountName, String authTokenType, Bundle bundle, String token) {
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, authTokenType);
bundle.putString(AccountManager.KEY_AUTHTOKEN, token);
return bundle;
}
AuthenticatorActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authenticator);
curSession = SessionSingleton.getInstance(getApplicationContext());
etMail = (EditText) findViewById(R.id.etMail);
etPass = (EditText) findViewById(R.id.etPass);
}
public void attemptLogin(View v) {
showWaitDialog("Signing in...");
curSession.getSessionInBackground(etMail.getText().toString(), authHandler);
}
AuthenticationHandler authHandler = new AuthenticationHandler() {
@Override
public void onSuccess(CognitoUserSession cognitoUserSession,
CognitoDevice cognitoDevice) {
Bundle result = new Bundle();
closeWaitDialog();
try {
Log.d(TAG, "> authHandler > onSuccess");
curSession.setSession(cognitoUserSession);
String token = curSession.getIdToken();
result.putString(AccountManager.KEY_AUTHTOKEN, token);
AccountManager accountManager = AccountManager.get(getApplicationContext());
Account account = new Account(etMail.getText().toString(),
getString(R.string.account_type));
accountManager.addAccountExplicitly(account, token, result);
accountManager.setAuthToken(account, "n/a", token);
setAccountAuthenticatorResult(result);
finish();
} catch (Exception e) {
showDialogMessage("ERROR", e.getLocalizedMessage());
}
}
@Override
public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String username) {
String password = etPass.getText().toString();
AuthenticationDetails authenticationDetails = new AuthenticationDetails(username, password, null);
authenticationContinuation.setAuthenticationDetails(authenticationDetails);
authenticationContinuation.continueTask();
}
@Override
public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) {
closeWaitDialog();
showDialogMessage("MFA is not supported", "");
}
@Override
public void authenticationChallenge(final ChallengeContinuation continuation) {
continuation.continueTask();
}
@Override
public void onFailure(Exception e) {
closeWaitDialog();
showDialogMessage("Sign-in failed", formatException(e));
}
};
SessionSingleton
private SessionSingleton(Context context) {
userPool = new CognitoUserPool(
context,
USER_POOL_ID,
CLIENT_ID,
CLIENT_SECRET,
new ClientConfiguration(),
USER_POOL_REGION);
}
void getSessionInBackground(String username, AuthenticationHandler authHandler) {
user = userPool.getUser(username);
user.getSessionInBackground(authHandler);
}
void setSession(CognitoUserSession session) {
this.session = session;
}
CognitoUserSession getSession() {
return session;
}
String getIdToken() {
return session.getIdToken().getJWTToken();
}