我正在开发基于XMPP的Android聊天客户端。它适用于Socket连接,并且要保持连接有效,需要定期ping服务器。为此我使用后台运行android服务。
在此服务中创建连接实例。此服务永远存在于后台,并执行ping和接收消息的工作。
一个问题是当聊天客户端从任务管理器,后台服务RESTARTS中被杀死时(它仍然继续运行,因为我已将其声明为START-STICKY)。
服务重新启动后,清除连接实例。这会导致我的问题,因为连接实例被清除,相当于连接丢失。
所以如果on on onStartCommand of service,我正在重新建立连接。
但要建立连接,我需要用户名和密码。我想将这些凭据保存在Android的AccountManager中。 (我认为在AccountManager中存储密码比使用SharedPreferrence或SQLite或FileSystem更安全)
1。由于我没有存储任何身份验证令牌,在我的情况下使用AccountManager是正确的选择吗?它真的比其他存储更安全吗?
2。有没有办法以身份管理器中存储身份验证令牌的方式存储连接实例?
在我关于客户经理的阅读中,我已经实施了AuthenticationService,AccountAuthenticator和AccountAuthenticator Activity,如下所示 -
AuthenticatationService:
public class AuthenticatationService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new AccountAuthenticator(this).getIBinder();
}
}
帐户身份验证员:
public class AccountAuthenticator extends AbstractAccountAuthenticator {
Context mContext;
public AccountAuthenticator(Context context) {
super(context);
mContext=context;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
Log.d("Mymsg", "AccountAuthenticator > addAccount() called with " + "response = [" + response + "], accountType = [" + accountType + "], authTokenType = [" + authTokenType + "], requiredFeatures = [" + requiredFeatures + "], options = [" + options + "]" + "---->START");
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), accountType);
intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
intent.putExtra(mContext.getString(R.string.intentdatakey_isaddingnewaccount), true);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Log.d("Mymsg", "AccountAuthenticator > addAccount() returned [bundle: " + bundle + "----> STOP");
return bundle;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
Log.d("Mymsg", "AccountAuthenticator > getAuthToken() called with " + "response = [" + response + "], account = [" + account + "], authTokenType = [" + authTokenType + "], options = [" + options + "]" + "---->START");
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
// Lets give another try to authenticate the user
if (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
authToken = password;
}
}
// If we get an authToken - we return it
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;
}
// 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);
intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), account.type);
intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Log.d("Mymsg", "AccountAuthenticator > getAuthToken() returned [bundle: " + bundle + "----> STOP");
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
}
登录活动:
public class LoginActivity extends AccountAuthenticatorActivity implements View.OnClickListener {
private EditText txtUsername;
private Button btnLogin;
private String username;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
txtUsername = (EditText) findViewById(R.id.txtUsername);
btnLogin = (Button) findViewById(R.id.btnLogin);
btnLogin.setOnClickListener(this);
progressDialog = new ProgressDialog(this);
// Connection and Login Response events
LocalBroadcastManager.getInstance(this).registerReceiver(connectionSuccessfulReceiver,
new IntentFilter(getString(R.string.broadcastmessage_connectionsuccessful)));
LocalBroadcastManager.getInstance(this).registerReceiver(connectionFailureReceiver,
new IntentFilter(getString(R.string.broadcastmessage_connectionfailure)));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLogin:
Log.d("Mymsg", "LoginActivity > onClick() called with " + "v = [" + v + "]" + "---->START");
username = txtUsername.getText().toString();
if (username.isEmpty()) {
Toast.makeText(this, "Please enter username", Toast.LENGTH_LONG);
return;
}
progressDialog.setMessage("Logging In...");
progressDialog.setCancelable(false);
progressDialog.show();
final Intent res = new Intent();
res.putExtra(AccountManager.KEY_ACCOUNT_NAME, username);
res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.accounttype));
res.putExtra(AccountManager.KEY_AUTHTOKEN, username);
res.putExtra(AccountManager.KEY_PASSWORD, username);
final Account account = new Account(username, getString(R.string.accounttype));
if (getIntent().getBooleanExtra(getString(R.string.intentdatakey_isaddingnewaccount), false)) {
String authtoken = username;
String authtokenType = getString(R.string.authtype);
// Creating the account on the device and setting the auth token we got
// (Not setting the auth token will cause another call to the server to authenticate the user)
AccountManager.get(this).addAccountExplicitly(account, username, null);
AccountManager.get(this).setAuthToken(account, authtokenType, authtoken);
} else {
AccountManager.get(this).setPassword(account, username);
}
setAccountAuthenticatorResult(res.getExtras());
setResult(RESULT_OK, res);
Log.d("Mymsg", "LoginActivity > onClick() ----> STOP");
break;
default:
break;
}
}
/**
* On Connection Successful
*/
public void onConnectionSuccessful() {
Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() called with " + "" + "---->START");
runOnUiThread(new Runnable() {
@Override
public void run() {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
});
finish();
Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() ----> STOP");
}
/**
* On Connection Failure
*
* @param message- error message
*/
public void onConnectionFailure(final String message) {
Log.d("Mymsg", "LoginActivity > onConnectionFailure() called with " + "message = [" + message + "]" + "---->START");
runOnUiThread(new Runnable() {
@Override
public void run() {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
Toast.makeText(LoginActivity.this, "Failed connecting: " + message, Toast.LENGTH_LONG).show();
}
});
Log.d("Mymsg", "LoginActivity > onConnectionFailure() ----> STOP");
}
/*
* Recievers for Connection Events
*/
private BroadcastReceiver connectionSuccessfulReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
onConnectionSuccessful();
Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() returned void ----> STOP");
}
};
private BroadcastReceiver connectionFailureReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
onConnectionFailure(intent.getStringExtra(getString(R.string.intentdatakey_failuremessage)));
Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() ----> STOP");
}
};
}
关于我检查的每个活动的恢复,如果连接为空,如果是,我已经启动了后台服务(我之前提到过维护连接并定期ping)。如果AccountManager中存在Account并且提供回调服务,则此服务将建立连接。
第3。我面临的问题是AccountManager中尚未出现用户名和密码。根据我的理解,getAuthToken是Authenticator中的入口点,如果auth令牌不存在则调用登录活动。但是,如果不存在用户名,则如何调用getAuthToken。需要建议。
以下是我的后台服务 -
public class XMPPListener extends Service implements ChatMessageListener, ChatManagerListener, ConnectionListener {
private static XMPPListener instance;
private NotificationCompat.Builder mBuilder;
private NotificationManager mNotificationManager;
private Context mContext;
private AbstractXMPPConnection connection;
private static boolean isConnected = false;
private static boolean isLoggedIn = false;
private User user;
public XMPPListener() {
Log.d("Mymsg", "XMPPListener > XMPPListener() called with " + "" + "---->START");
mContext = this;
instance = this;
Log.d("Mymsg", "XMPPListener > XMPPListener() > this: " + this);
//To show notification when app is not foreground
mBuilder = new NotificationCompat.Builder(mContext);
Log.d("Mymsg", "XMPPListener > XMPPListener() ----> STOP");
}
/**
* Get Singleton instance
*
* @return instance
*/
public static XMPPListener getInstance(Context context) {
return instance;
}
@Override
public void processMessage(Chat chat, Message message) {
Log.d("Mymsg", "XMPPListener > processMessage() called with " + "message = [" + message + "]" + "---->START");
try {
// Make entry of message in local database
makeEntryOfMessageInLocalDatabase(chat, message);
// Show Notification
showNotification(chat, message);
// Broadcast message that new message received
LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_messagereceived)));
} catch (SQLException e) {
e.printStackTrace();
}
Log.d("Mymsg", "XMPPListener > processMessage() returned void ----> STOP");
}
/**
* Show Notification
*
* @param chat
* @param message
*/
private void showNotification(Chat chat, Message message) {
mBuilder.setSmallIcon(R.drawable.notification_icon);
mBuilder.setContentTitle("@" + chat.getParticipant() + " messaged");
mBuilder.setContentText(message.getBody());
Intent resultIntent = new Intent(mContext, ChatWindowActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
stackBuilder.addParentStack(ChatWindowActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
// notificationID allows you to update the notification later on.
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
/**
* Make entry of message in Local Database
*
* @param chat
* @param message
* @throws SQLException
*/
private void makeEntryOfMessageInLocalDatabase(Chat chat, Message message) throws SQLException {
Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() called with " + "chat = [" + chat + "], message = [" + message + "]" + "---->START");
ChatMessage chatMessage = new ChatMessage();
// This is received message
chatMessage.setSent(false);
chatMessage.setThreadId(chat.getThreadID());
chatMessage.setChatmessage(message.getBody());
chatMessage.setTimeStamp(new Date());
chatMessage.setIsRead(false);
ChatThread chatThread = new ChatThread();
chatThread.setThreadId(chat.getThreadID());
chatThread.setUsername(chat.getParticipant());
chatThread.setLastChatmessage(message.getBody());
chatThread.setLastMessageTimeStamp(new Date());
chatThread.setLastMessageSent(false);
chatThread.setUnreadMessageCount(SampleChatClientAppDataOperations.getInstance(mContext).getUnReadMessageCountInThread(chat.getThreadID()));
// Make database entry
SampleChatClientAppData sampleChatClientAppData = new SampleChatClientAppData(mContext);
// Update messages table
sampleChatClientAppData.getChatMessageDao().create(chatMessage);
// Update Chat list table
sampleChatClientAppData.getChatThreadDao().update(chatThread);
Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() returned void ----> STOP");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d("Mymsg", "XMPPListener > onCreate() called with " + "" + "---->START");
super.onCreate();
Log.d("Mymsg", "XMPPListener > onCreate() ----> STOP");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("Mymsg", "XMPPListener > onStartCommand() called with " + "intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]" + "---->START");
if (connection == null || !connection.isConnected() || !connection.isAuthenticated()) {
new GetAuthToken().execute();
}
int returnValue = super.onStartCommand(intent, flags, startId);
Log.d("Mymsg", "XMPPListener > onStartCommand() returned [returnValue: " + returnValue + "----> STOP");
return returnValue;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
Log.d("Mymsg", "XMPPListener > onTaskRemoved() called with " + "rootIntent = [" + rootIntent + "]" + "---->START");
super.onTaskRemoved(rootIntent);
Log.d("Mymsg", "XMPPListener > onTaskRemoved() ----> STOP");
}
@Override
public void onDestroy() {
Log.d("Mymsg", "XMPPListener > onDestroy() called with " + "" + "---->START");
super.onDestroy();
Log.d("Mymsg", "XMPPListener > onDestroy() ----> STOP");
}
/**
* Getter for connection
*
* @return instance of connection
*/
public AbstractXMPPConnection getConnection() {
return connection;
}
@Override
public void chatCreated(final Chat chat, boolean createdLocally) {
Log.d("Mymsg", "XMPPListener > chatCreated() called with " + "chat = [" + chat + "], createdLocally = [" + createdLocally + "]" + "---->START");
if (!createdLocally) {
// Register listener
chat.addMessageListener(XMPPListener.getInstance(this));
checkIfChatThreadExistForUser(chat.getParticipant(), new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
super.handleMessage(msg);
ArrayList<ChatThread> chatThread;
try {
chatThread = (ArrayList<ChatThread>) msg.obj;
Log.d("Mymsg", "chatThread: " + chatThread);
if (chatThread.isEmpty()) {
// If first time chatting with this user create new thread;
createChatThreadInLocalDb(chat);
}
} catch (ClassCastException e) {
e.printStackTrace();
}
Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() returned null----> STOP");
}
}, new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
}
});
}
Log.d("Mymsg", "XMPPListener > chatCreated() returned void----> STOP");
}
/**
* Create chat thread in local db
*
* @param chat
*/
public void createChatThreadInLocalDb(Chat chat) {
Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() called with " + "chat = [" + chat + "]" + "---->START");
ChatThread newChatThread = new ChatThread();
newChatThread.setUsername(chat.getParticipant());
newChatThread.setThreadId(chat.getThreadID());
Datasource.addChatThread(this, newChatThread, new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
Log.d("Mymsg", "XMPPListener > addChatThread > createChatThreadInLocalDb > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
super.handleMessage(msg);
Integer newChatThreadCreated = 0;
try {
newChatThreadCreated = (Integer) msg.obj;
if (newChatThreadCreated > 0) {
// Broadcast that chat is created
LocalBroadcastManager.getInstance(XMPPListener.this).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_chatcreated)));
}
} catch (ClassCastException e) {
e.printStackTrace();
}
Log.d("Mymsg", "XMPPListener > addChatThread > handleMessage() ----> STOP");
}
}, new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
}
});
Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() returned null----> STOP");
}
/**
* Checks if thread exist for the username else
* create new one
*
* @param fullUsername
*/
public void checkIfChatThreadExistForUser(final String fullUsername, final Handler successHandler, final Handler failureHandler) {
Datasource.getChatThreadOfUser(this, fullUsername, successHandler, failureHandler);
}
// Internal sync task for connecting to XMPP server
class ConnectXMPP extends AsyncTask<User, Void, Exception> {
@Override
protected Exception doInBackground(User... params) {
Log.d("Mymsg", "ConnectXMPP > doInBackground() called with " + "params = [" + params + "]" + "---->START");
User user = params[0];
Log.d("Mymsg", "user: " + user);
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword(user.getUsername(), user.getPassword())
.setServiceName(mContext.getString(R.string.openfire_host))
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setDebuggerEnabled(true)
.build();
SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
SASLAuthentication.unBlacklistSASLMechanism("PLAIN");
connection = new XMPPTCPConnection(config);
try {
connection.connect();
isConnected = connection.isConnected();
Log.d("Mymsg", "isConnected: " + isConnected);
connection.login();
isLoggedIn = connection.isAuthenticated();
Log.d("Mymsg", "isLoggedIn: " + isLoggedIn);
connection.addConnectionListener(XMPPListener.this);
// Broadcast message that new message received
LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.broadcastmessage_connectionsuccessful)));
} catch (SmackException e) {
e.printStackTrace();
Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
} catch (IOException e) {
e.printStackTrace();
Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
} catch (XMPPException e) {
e.printStackTrace();
Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
}
return null;
}
@Override
protected void onPostExecute(Exception e) {
super.onPostExecute(e);
}
}
class GetAuthToken extends AsyncTask<Void, Void, Exception> {
private User user;
@Override
protected Exception doInBackground(Void... params) {
String username = "";
String password = "";
AccountManager accountManager = AccountManager.get(XMPPListener.this);
final Account[] accounts = accountManager.getAccountsByType(getString(R.string.accounttype));
Account account;
if (accounts.length <= 0) {
account = new Account(username, getString(R.string.accounttype));
} else {
account = accounts[0];
}
try {
Bundle bundle = accountManager.getAuthToken(account, mContext.getString(R.string.authtype), true, null, null).getResult();
password = (String) bundle.get(AccountManager.KEY_AUTHTOKEN);
Log.d("Mymsg", "password: " + password);
username = password;
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
if (username.isEmpty() || password.isEmpty()) {
Intent broadCastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
broadCastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), getString(R.string.errormessage_usercredentialmissing));
LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadCastIntent);
return null;
}
user = new User();
user.setUsername(username);
user.setPassword(password);
return null;
}
@Override
protected void onPostExecute(Exception e) {
super.onPostExecute(e);
if (user != null) {
// Connect XMPP
new ConnectXMPP().execute(user);
}
}
}
}
答案 0 :(得分:1)
<强> 1。由于我没有存储任何身份验证令牌,在我的情况下使用AccountManager是正确的选择吗?它真的比其他存储更安全吗?
本身并不安全。客户经理只是在SQLite数据库中的“plaintext”中存储用户名,密码和authtokens。 因此,如果您具有root访问权限,则通常可以轻松访问这些值。
如果您打算使用同步适配器机制,或者打算同步到某些内容提供商(如联系人提供商或日历提供商),则需要使用身份验证器。
除此之外,它为多个帐户提供了轻松支持,并提供了一个干净的界面来检索身份验证凭据。后者很好地将帐户管理/身份验证部分与应用程序的其余部分分离。
回答第一个问题:这取决于。如果您不熟悉Authenticators,并且您只是寻求更好的安全性,那么在不使用Authenticator的情况下,您可能会感觉更好。
第3。我面临的问题是AccountManager中尚未出现用户名和密码。根据我的理解,getAuthToken是Authenticator中的入口点,如果auth令牌不存在则调用登录活动。但是,如果不存在用户名,则如何调用getAuthToken。需要建议。
这不是它的工作原理。您只能为现有帐户拨打getAuthToken
。 getAuthToken
仅在验证者返回Intent
时才调用活动。这样做的一个用例是使用OAuth2和用户撤销了应用的访问权限。在这种情况下,您需要提示用户再次授予访问权限。
请注意,其他应用程序也可能会请求auth-token(因为他们具有所需的权限),因此请确保不要将纯文本密码作为auth-token返回,或确保其他应用程序赢得'从验证者那里收到一个令牌。
因此,在调用Intent
时从验证者返回getAuthToken
的另一个用例是,如果要提示用户进行许可。
所以这就是它的完成方式:
您的应用应首先检查是否有现有帐户(使用getAccountsByType)。如果有,您可以拨打getAuthToken
,如果有多个帐户,您通常会询问用户如果之前没有选择任何帐户,那么您为该帐户拨打getAuthToken
,否则请致电addAccount让用户先添加帐户。
如果您不想授予其他应用访问您的纯文本密码的权限,请考虑使用setPassword和getPassword而不是使用authtoken。密码是您的应用程序专用的密码,其他应用程序无法检索(嗯,至少更难)。或者考虑根本不使用Authenticator。在您的情况下,可能不值得增加复杂性。