我正在创建一个使用用户名/密码连接到服务器的应用程序,我想启用“保存密码”选项,这样用户就不必在每次应用程序启动时输入密码。
我试图用共享偏好来做,但不确定这是否是最好的解决方案。
我很感激任何关于如何在Android应用程序中存储用户值/设置的建议。
我正在创建一个使用用户名/密码连接到服务器的应用程序,我想启用“保存密码”选项,这样用户就不必在每次应用程序启动时输入密码。
我试图用共享偏好来做,但不确定这是否是最好的解决方案。
我很感激任何关于如何在Android应用程序中存储用户值/设置的建议。
当前回答
我将加入这个圈子,只是为了谈谈在Android上保护密码的一般问题。在Android上,设备二进制文件应该被认为是被破坏的——这对于任何终端应用程序都是一样的,因为它是直接由用户控制的。从概念上讲,黑客可以使用对二进制文件的必要访问来反编译它,并找出你的加密密码等等。
因此,如果安全是你主要关注的问题,我想提出两个建议:
1)不要保存实际密码。存储已授予的访问令牌,并使用该访问令牌和话机的签名对会话服务器端进行认证。这样做的好处是,您可以使令牌有一个有限的持续时间,您不会损害原始密码,并且您有一个很好的签名,您可以使用它来关联以后的流量(例如检查入侵尝试和使令牌无效,使其无用)。
2)利用2因素认证。这可能更令人讨厌和侵入,但在某些合规情况下是不可避免的。
其他回答
我知道这有点像巫术,但你应该使用Android AccountManager。它是专门为这个场景构建的。这有点麻烦,但它所做的一件事是,如果SIM卡发生变化,本地凭据就会失效,所以如果有人刷你的手机,把一个新的SIM卡扔进去,你的凭据不会被泄露。
这也为用户提供了一种快速简单的方法,可以从一个地方访问(可能还会删除)他们在设备上的任何帐户的存储凭据。
SampleSyncAdapter就是一个使用存储帐户凭据的例子。
共享首选项是存储应用程序数据的最简单方法。但任何人都可以通过应用程序管理器清除我们共享的首选项数据。所以我不认为它对我们的应用是完全安全的。
正如其他人已经指出的,你可以使用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
一般来说,SharedPreferences是存储首选项的最佳选择,所以一般来说,我推荐使用这种方法来保存应用程序和用户设置。
这里唯一需要关注的是你节省了什么。存储密码总是一件很棘手的事情,我对以明文形式存储密码特别谨慎。Android架构是这样的,你的应用程序的SharedPreferences是沙盒的,以防止其他应用程序能够访问这些值,所以有一些安全性,但对手机的物理访问可能允许访问这些值。
如果可能的话,我会考虑修改服务器,使用协商的令牌来提供访问,比如OAuth。或者,您可能需要构造某种加密存储,尽管这不是简单的。至少,要确保在将密码写入磁盘之前对其进行了加密。
我就是这么做的。
这不会在严格模式下给出错误。
public class UserPreferenceUtil
{
private static final String THEME = "THEME";
private static final String LANGUAGE = "LANGUAGE";
public static String getLanguagePreference(Context context)
{
String lang = getPreferenceByKey(context,LANGUAGE);
if( lang==null || "System".equalsIgnoreCase(lang))
{
return null;
}
return lang;
}
public static void saveLanguagePreference(Context context,String value)
{
savePreferenceKeyValue(context, LANGUAGE,value);
}
public static String getThemePreference(Context context)
{
return getPreferenceByKey(context,THEME);
}
public static void saveThemePreference(Context context, String value)
{
savePreferenceKeyValue(context,THEME,value);
}
public static String getPreferenceByKey(Context context, String preferenceKey )
{
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String value = sharedPreferences.getString(preferenceKey, null);
return value;
}
private static void savePreferenceKeyValue(Context context, String preferenceKey, String value)
{
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(preferenceKey,value);
editor.apply();
}
}
我的应用程序不需要密码。但是,我不会保存密码或加密密码,而是保存单向散列。当用户登录时,我将以同样的方式哈希输入,并将其与存储的哈希匹配。