在Android应用程序中存储用户设置的最合适方法是什么

本文翻译自:What is the most appropriate way to store user settings in Android application

I am creating an application which connects to the server using username/password and I would like to enable the option "Save password" so the user wouldn't have to type the password each time the application starts. 我正在创建一个使用用户名/密码连接到服务器的应用程序,我想启用“保存密码”选项,这样用户每次启动应用程序时都不必输入密码。

I was trying to do it with Shared Preferences but am not sure if this is the best solution. 我试图使用共享偏好设置,但不确定这是否是最佳解决方案。

I would appreciate any suggestion on how to store user values/settings in Android application. 我将不胜感激任何有关如何在Android应用程序中存储用户值/设置的建议。


#1楼

参考:https://stackoom.com/question/3ISz/在Android应用程序中存储用户设置的最合适方法是什么


#2楼

I know this is a little bit of necromancy, but you should use the Android AccountManager . 我知道这有点神秘,但你应该使用Android AccountManager It's purpose-built for this scenario. 它是专为此方案而构建的。 It's a little bit cumbersome but one of the things it does is invalidate the local credentials if the SIM card changes, so if somebody swipes your phone and throws a new SIM in it, your credentials won't be compromised. 这有点麻烦,但如果SIM卡发生变化,它所做的一件事就是使本地凭证失效,所以如果有人刷你的手机并在其中抛出一个新的SIM卡,你的凭证就不会受到损害。

This also gives the user a quick and easy way to access (and potentially delete) the stored credentials for any account they have on the device, all from one place. 这也为用户提供了一种快速简便的方法,可以从一个位置访问(并可能删除)他们在设备上拥有的任何帐户的存储凭据。

SampleSyncAdapter is an example that makes use of stored account credentials. SampleSyncAdapter是一个使用存储帐户凭据的示例。


#3楼

Okay; 好的; it's been a while since the answer is kind-of mixed, but here's a few common answers. 已经有一段时间了,因为答案有点混乱,但这里有一些常见的答案。 I researched this like crazy and it was hard to build a good answer 我研究这个就像疯了一样,很难建立一个好的答案

  1. The MODE_PRIVATE method is considered generally safe, if you assume that the user didn't root the device. 如果您假设用户没有root设备,则认为MODE_PRIVATE方法通常是安全的。 Your data is stored in plain text in a part of the file system that can only be accessed by the original program. 您的数据以纯文本格式存储在文件系统的一部分中,只能由原始程序访问。 This makings grabbing the password with another app on a rooted device easy. 这个makings很容易在root设备上使用另一个应用程序获取密码。 Then again, do you want to support rooted devices? 那么,你想支持root设备吗?

  2. AES is still the best encryption you can do. AES仍然是您可以做的最佳加密。 Remember to look this up if you are starting a new implementation if it's been a while since I posted this. 如果你开始一个新的实现,如果我发布这个已经有一段时间了,请记得查看这个。 The largest issue with this is "What to do with the encryption key?" 最大的问题是“如何处理加密密钥?”

So, now we are at the "What to do with the key?" 那么,现在我们正处于“如何处理密钥?” portion. 一部分。 This is the hard part. 这是困难的部分。 Getting the key turns out to be not that bad. 获得密钥并没有那么糟糕。 You can use a key derivation function to take some password and make it a pretty secure key. 您可以使用密钥派生函数来获取一些密码并使其成为非常安全的密钥。 You do get into issues like "how many passes do you do with PKFDF2?", but that's another topic 您确实会遇到诸如“您使用PKFDF2传递多少次?”这样的问题,但这是另一个主题

  1. Ideally, you store the AES key off the device. 理想情况下,您将AES密钥存储在设备上。 You have to figure out a good way to retrieve the key from the server safely, reliably, and securely though 您必须找到一种安全,可靠和安全地从服务器检索密钥的好方法

  2. You have a login sequence of some sort (even the original login sequence you do for remote access). 您有某种登录顺序(甚至是您为远程访问所做的原始登录顺序)。 You can do two runs of your key generator on the same password. 您可以使用相同的密码执行两次密钥生成器运行。 How this works is that you derive the key twice with a new salt and a new secure initialization vector. 这是如何工作的,您使用新的盐和新的安全初始化向量导出密钥两次。 You store one of those generated passwords on the device, and you use the second password as the AES key. 您将其中一个生成的密码存储在设备上,并使用第二个密码作为AES密钥。

When you log in, you re-derive the key on the local login and compare it to the stored key. 登录时,您将在本地登录名上重新导出密钥并将其与存储的密钥进行比较。 Once that is done, you use derive key #2 for AES. 完成后,您将使用派生密钥#2进行AES。

  1. Using the "generally safe" approach, you encrypt the data using AES and store the key in MODE_PRIVATE. 使用“通常安全”方法,您使用AES加密数据并将密钥存储在MODE_PRIVATE中。 This is recommended by a recent-ish Android blog post. 这是最近推出的Android博客文章推荐的。 Not incredibly secure, but way better for some people over plain text 不是非常安全,但对于一些人来说比普通文本更好

You can do a lot of variations of these. 你可以做很多这些变化。 For example, instead of a full login sequence, you can do a quick PIN (derived). 例如,您可以执行快速PIN(派生),而不是完整登录序列。 The quick PIN might not be as secure as a full login sequence, but it's many times more secure than plain text 快速PIN可能不如完整登录序列安全,但它比纯文本安全多倍


#4楼

This answer is based on a suggested approach by Mark. 这个答案是基于马克建议的方法。 A custom version of the EditTextPreference class is created which converts back and forth between the plain text seen in the view and an encrypted version of the password stored in the preferences storage. 创建EditTextPreference类的自定义版本,该类在视图中看到的纯文本和存储在首选项存储中的密码的加密版本之间来回转换。

As has been pointed out by most who have answered on this thread, this is not a very secure technique, although the degree of security depends partly on the encryption/decryption code used. 正如大多数已经回答过这个问题的人所指出的那样,这不是一种非常安全的技术,尽管安全程度部分取决于所使用的加密/解密代码。 But it's fairly simple and convenient, and will thwart most casual snooping. 但它相当简单方便,并且会阻碍大多数偶然窥探。

Here is the code for the custom EditTextPreference class: 以下是自定义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);
    }
}

This shows how it can be used - this is the "items" file that drives the preferences display. 这显示了如何使用它 - 这是驱动首选项显示的“项目”文件。 Note it contains three ordinary EditTextPreference views and one of the custom EditPasswordPreference views. 请注意,它包含三个普通的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>

As for the actual encryption/decryption, that is left as an exercise for the reader. 至于实际的加密/解密,这留给读者练习。 I'm currently using some code based on this article http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , although with different values for the key and the initialization vector. 我目前正在使用基于这篇文章的一些代码http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ ,尽管有不同的值用于密钥和初始化向量。


#5楼

You can also check out this little lib, containing the functionality you mention. 您还可以查看这个包含您提到的功能的小lib。

https://github.com/kovmarci86/android-secure-preferences https://github.com/kovmarci86/android-secure-preferences

It is similar to some of the other aproaches here. 它类似于其他一些方法。 Hope helps :) 希望有帮助:)


#6楼

shared preferences is easiest way to store our application data. 共享首选项是存储应用程序数据的最简单方法。 but it is possible that anyone can clear our shared preferences data through application manager.so i don't think it is completely safe for our application. 但是有可能任何人都可以通过应用程序管理器清除我们的共享首选项数据。所以我认为它对我们的应用程序来说并不完全安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值