我正在创建一个使用用户名/密码连接到服务器的应用程序,我想启用“保存密码”选项,这样用户就不必在每次应用程序启动时输入密码。
我试图用共享偏好来做,但不确定这是否是最好的解决方案。
我很感激任何关于如何在Android应用程序中存储用户值/设置的建议。
我正在创建一个使用用户名/密码连接到服务器的应用程序,我想启用“保存密码”选项,这样用户就不必在每次应用程序启动时输入密码。
我试图用共享偏好来做,但不确定这是否是最好的解决方案。
我很感激任何关于如何在Android应用程序中存储用户值/设置的建议。
当前回答
我使用Android KeyStore在ECB模式下使用RSA加密密码,然后将其保存在SharedPreferences中。
当我想要回密码时,我从SharedPreferences中读取加密的密码,并使用KeyStore对其解密。
使用这个方法,你可以生成一个公共/私有密钥对,其中私有密钥对由Android安全存储和管理。
这里有一个关于如何做到这一点的链接:Android KeyStore教程
其他回答
正如其他人已经指出的,你可以使用SharedPreferences一般,但如果你想存储加密的数据有点不方便。幸运的是,现在有一种更简单、更快速的方法来加密数据,因为有一个SharedPreferences的实现可以加密密钥和值。你可以在Android JetPack Security中使用EncryptedSharedPreferences。
只需在build.gradle中添加AndroidX Security:
implementation 'androidx.security:security-crypto:1.0.0-rc01'
你可以这样使用它:
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();
详情请访问:https://android-developers.googleblog.com/2020/02/data-encryption-on-android-with-jetpack.html
官方文档:https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
我使用Android KeyStore在ECB模式下使用RSA加密密码,然后将其保存在SharedPreferences中。
当我想要回密码时,我从SharedPreferences中读取加密的密码,并使用KeyStore对其解密。
使用这个方法,你可以生成一个公共/私有密钥对,其中私有密钥对由Android安全存储和管理。
这里有一个关于如何做到这一点的链接:Android KeyStore教程
关于在Android Activity中存储单个首选项的最简单的方法是这样做:
Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();
如果您担心这些密码的安全性,那么您可以在存储密码之前对其进行加密。
一般来说,SharedPreferences是存储首选项的最佳选择,所以一般来说,我推荐使用这种方法来保存应用程序和用户设置。
这里唯一需要关注的是你节省了什么。存储密码总是一件很棘手的事情,我对以明文形式存储密码特别谨慎。Android架构是这样的,你的应用程序的SharedPreferences是沙盒的,以防止其他应用程序能够访问这些值,所以有一些安全性,但对手机的物理访问可能允许访问这些值。
如果可能的话,我会考虑修改服务器,使用协商的令牌来提供访问,比如OAuth。或者,您可能需要构造某种加密存储,尽管这不是简单的。至少,要确保在将密码写入磁盘之前对其进行了加密。
这个答案是基于Mark建议的方法。创建EditTextPreference类的自定义版本,它在视图中看到的纯文本和存储在首选项存储中的密码的加密版本之间来回转换。
正如大多数在这篇文章中回答问题的人所指出的,这不是一种非常安全的技术,尽管安全程度在一定程度上取决于所使用的加密/解密代码。但它相当简单和方便,并将阻止大多数随意窥探。
下面是自定义EditTextPreference类的代码:
package com.Merlinia.OutBack_Client;
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.util.Base64;
import com.Merlinia.MEncryption_Main.MEncryptionUserPassword;
/**
* This class extends the EditTextPreference view, providing encryption and decryption services for
* OutBack user passwords. The passwords in the preferences store are first encrypted using the
* MEncryption classes and then converted to string using Base64 since the preferences store can not
* store byte arrays.
*
* This is largely copied from this article, except for the encryption/decryption parts:
* https://groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M
*/
public class EditPasswordPreference extends EditTextPreference {
// Constructor - needed despite what compiler says, otherwise app crashes
public EditPasswordPreference(Context context) {
super(context);
}
// Constructor - needed despite what compiler says, otherwise app crashes
public EditPasswordPreference(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
// Constructor - needed despite what compiler says, otherwise app crashes
public EditPasswordPreference(Context context, AttributeSet attributeSet, int defaultStyle) {
super(context, attributeSet, defaultStyle);
}
/**
* Override the method that gets a preference from the preferences storage, for display by the
* EditText view. This gets the base64 password, converts it to a byte array, and then decrypts
* it so it can be displayed in plain text.
* @return OutBack user password in plain text
*/
@Override
public String getText() {
String decryptedPassword;
try {
decryptedPassword = MEncryptionUserPassword.aesDecrypt(
Base64.decode(getSharedPreferences().getString(getKey(), ""), Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
decryptedPassword = "";
}
return decryptedPassword;
}
/**
* Override the method that gets a text string from the EditText view and stores the value in
* the preferences storage. This encrypts the password into a byte array and then encodes that
* in base64 format.
* @param passwordText OutBack user password in plain text
*/
@Override
public void setText(String passwordText) {
byte[] encryptedPassword;
try {
encryptedPassword = MEncryptionUserPassword.aesEncrypt(passwordText);
} catch (Exception e) {
e.printStackTrace();
encryptedPassword = new byte[0];
}
getSharedPreferences().edit().putString(getKey(),
Base64.encodeToString(encryptedPassword, Base64.DEFAULT))
.commit();
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue)
getEditText().setText(getText());
else
super.onSetInitialValue(restoreValue, defaultValue);
}
}
这显示了如何使用它-这是驱动首选项显示的“items”文件。注意,它包含三个普通EditTextPreference视图和一个自定义EditPasswordPreference视图。
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="@string/useraccountname_key"
android:title="@string/useraccountname_title"
android:summary="@string/useraccountname_summary"
android:defaultValue="@string/useraccountname_default"
/>
<com.Merlinia.OutBack_Client.EditPasswordPreference
android:key="@string/useraccountpassword_key"
android:title="@string/useraccountpassword_title"
android:summary="@string/useraccountpassword_summary"
android:defaultValue="@string/useraccountpassword_default"
/>
<EditTextPreference
android:key="@string/outbackserverip_key"
android:title="@string/outbackserverip_title"
android:summary="@string/outbackserverip_summary"
android:defaultValue="@string/outbackserverip_default"
/>
<EditTextPreference
android:key="@string/outbackserverport_key"
android:title="@string/outbackserverport_title"
android:summary="@string/outbackserverport_summary"
android:defaultValue="@string/outbackserverport_default"
/>
</PreferenceScreen>
至于实际的加密/解密,留给读者练习。我目前正在使用基于本文的一些代码http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/,尽管键和初始化向量的值不同。