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

时间:2021-08-09 21:45:03

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应用中存储用户值/设置的建议,我将不胜感激。

13 个解决方案

#1


221  

In general SharedPreferences are your best bet for storing preferences, so in general I'd recommend that approach for saving application and user settings.

一般来说,SharedPreferences是存储首选项的最佳选择,因此通常我建议使用这种方法来保存应用程序和用户设置。

The only area of concern here is what you're saving. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text. The Android architecture is such that your application's SharedPreferences are sandboxed to prevent other applications from being able to access the values so there's some security there, but physical access to a phone could potentially allow access to the values.

这里唯一值得关注的是你在储蓄什么。密码总是一件很棘手的事情,我特别担心把它们存储为明文。Android的体系结构是这样的:您的应用程序的SharedPreferences被沙箱化,以防止其他应用程序能够访问这些值,因此存在一些安全性,但是对电话的物理访问可能允许访问这些值。

If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth. Alternatively you may need to construct some sort of cryptographic store, though that's non-trivial. At the very least, make sure you're encrypting the password before writing it to disk.

如果可能的话,我将考虑修改服务器以使用协商过的令牌来提供访问,类似OAuth。或者,您可能需要构建某种类型的加密存储,尽管这并非易事。至少,在将密码写入磁盘之前,要确保对密码进行了加密。

#2


199  

I agree with Reto and fiXedd. Objectively speaking it doesn't make a lot of sense investing significant time and effort into encrypting passwords in SharedPreferences since any attacker that has access to your preferences file is fairly likely to also have access to your application's binary, and therefore the keys to unencrypt the password.

我同意Reto和fiXedd。客观地说,在SharedPreferences中花费大量时间和精力对密码进行加密没有多大意义,因为任何访问您的首选项文件的攻击者都很可能访问您的应用程序的二进制文件,因此也有可能访问密码的密钥。

However, that being said, there does seem to be a publicity initiative going on identifying mobile applications that store their passwords in cleartext in SharedPreferences and shining unfavorable light on those applications. See http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ and http://viaforensics.com/appwatchdog for some examples.

不过,话虽如此,似乎确实有一项宣传活动正在进行,即识别将密码以共享首选项的方式存储在cleartext中的移动应用程序,并对这些应用程序发出不利的信号。查看http://blogs.wsj.com/digits/2011/06/08/some-top-app - putdata -risk/和http://viasics.com/appwatchdog为您提供一些示例。

While we need more attention paid to security in general, I would argue that this sort of attention on this one particular issue doesn't actually significantly increase our overall security. However, perceptions being as they are, here's a solution to encrypt the data you place in SharedPreferences.

虽然我们需要更多地关注一般的安全问题,但我认为,这种对这一特定问题的关注实际上并没有显著增加我们的整体安全。但是,由于感知是真实的,这里有一个解决方案来加密您在SharedPreferences中放置的数据。

Simply wrap your own SharedPreferences object in this one, and any data you read/write will be automatically encrypted and decrypted. eg.

只需将您自己的SharedPreferences对象包装在这个对象中,您所读/写的任何数据都将被自动加密和解密。如。

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

Here's the code for the class:

下面是这个类的代码:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}

#3


28  

About the simplest way to store a single preference in an Android Activity is to do something like this:

在Android活动中存储单一偏好的最简单方法是这样做:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

If you're worried about the security of these then you could always encrypt the password before storing it.

如果您担心这些密码的安全性,那么您可以在存储密码之前对其进行加密。

#4


9  

Using the snippet provided by Richard, you can encrypt the password before saving it. The preferences API however doesn't provide an easy way to intercept the value and encrypt it - you can block it being saved via an OnPreferenceChange listener, and you theoretically could modify it through a preferenceChangeListener, but that results in an endless loop.

使用Richard提供的代码片段,您可以在保存密码之前对密码进行加密。不过,preferences API并没有提供一种简单的方法来拦截值并对其进行加密——您可以通过OnPreferenceChange侦听器阻止它被保存,并且理论上可以通过preferenceChangeListener对其进行修改,但这将导致无休止的循环。

I had earlier suggested adding a "hidden" preference in order to accomplish this. It's definitely not the best way. I'm going to present two other options that I consider to be more viable.

我之前曾建议增加一个“隐藏”的偏好来完成这个任务。这绝对不是最好的方法。我将提出另外两个我认为更可行的选择。

First, the simplest, is in a preferenceChangeListener, you can grab the entered value, encrypt it, and then save it to an alternative preferences file:

首先,最简单的是在preferenceChangeListener中,您可以获取输入的值,对其进行加密,然后将其保存到另一个首选项文件中:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

The second way, and the way I now prefer, is to create your own custom preference, extending EditTextPreference, @Override'ing the setText() and getText() methods, so that setText() encrypts the password, and getText() returns null.

第二种方式,也是我现在喜欢的方式,是创建您自己的自定义首选项,扩展EditTextPreference, @Override' setText()和getText()方法,以便setText()加密密码,getText()返回null。

#5


5  

I know this is a little bit of necromancy, but you should use the 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.

我知道这有点死角,但是你应该使用Android AccountManager。它的目的是为这个场景构建的。这有点麻烦,但它所做的一件事是,如果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是一个使用存储帐户凭据的示例。

#6


5  

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. 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. Then again, do you want to support rooted devices?

    MODE_PRIVATE方法通常被认为是安全的,如果您假设用户没有root用户该设备。您的数据以纯文本的形式存储在文件系统的某个部分中,该部分只能由原始程序访问。这使得在根设备上使用另一个应用程序获取密码变得很容易。再说一遍,你想支持有根设备吗?

  2. AES is still the best encryption you can do. 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?"

    AES仍然是最好的加密技术。如果您正在启动一个新实现(如果我发布这篇文章已经有一段时间了),请记住要查找这个。最大的问题是“如何处理加密密钥?”

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

那么,现在我们就到了“如何处理密钥”部分。这是最难的部分。拿到钥匙并没有那么糟糕。您可以使用一个密钥派生函数来获取一些密码并使其成为一个非常安全的密钥。你会遇到这样的问题:“你用pkfdfdf2做了多少次传递?”但那是另一个话题

  1. Ideally, you store the AES key off the device. You have to figure out a good way to retrieve the key from the server safely, reliably, and securely though

    理想情况下,将AES密钥存储在设备上。您必须找到一种从服务器安全地、可靠地、安全地检索密钥的好方法。

  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.

    您有某种登录序列(甚至是远程访问的原始登录序列)。您可以在相同的密码上运行两次密钥生成器。它的工作原理是使用新的salt和新的安全初始化向量两次派生密钥。您将其中一个生成的密码存储在设备上,并使用第二个密码作为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. This is recommended by a recent-ish Android blog post. Not incredibly secure, but way better for some people over plain text
  2. 使用“一般安全”方法,使用AES加密数据,并将密钥存储在MODE_PRIVATE中。这是由Android博客推荐的。不是很安全,但对一些人来说,这比纯文本要好得多。

You can do a lot of variations of these. For example, instead of a full login sequence, you can do a quick PIN (derived). The quick PIN might not be as secure as a full login sequence, but it's many times more secure than plain text

你可以做很多变化。例如,您可以使用一个快速大头针(派生的)代替完整的登录序列。快速大头针可能不如完整的登录顺序安全,但它比纯文本安全得多

#7


5  

I'll throw my hat into the ring just to talk about securing passwords in general on Android. On Android, the device binary should be considered compromised - this is the same for any end application which is in direct user control. Conceptually, a hacker could use the necessary access to the binary to decompile it and root out your encrypted passwords and etc.

我将把我的帽子扔进戒指,只是为了讨论如何在Android上保护密码。在Android上,设备二进制文件应该被认为是有害的——这对于任何直接由用户控制的终端应用程序都是一样的。从概念上讲,黑客可以使用对二进制文件的必要访问来对其进行分解,并将您的加密密码等删除。

As such there's two suggestions I'd like to throw out there if security is a major concern for you:

因此,如果安全是你主要关心的问题,我想提出两个建议:

1) Don't store the actual password. Store a granted access token and use the access token and the signature of the phone to authenticate the session server-side. The benefit to this is that you can make the token have a limited duration, you're not compromising the original password and you have a good signature that you can use to correlate to traffic later (to for instance check for intrusion attempts and invalidate the token rendering it useless).

1)不要存储实际的密码。存储已授予的访问令牌,并使用访问令牌和电话的签名对会话服务器端进行身份验证。这样做的好处是,您可以使令牌具有有限的持续时间,您不会损害原始密码,并且您有一个良好的签名,您可以在以后使用它来关联到流量(例如检查入侵尝试并使令令牌失效)。

2) Utilize 2 factor authentication. This may be more annoying and intrusive but for some compliance situations unavoidable.

2)利用2因素认证。这可能会更烦人、更烦人,但对于某些合规情况来说,这是不可避免的。

#8


2  

You can also check out this little lib, containing the functionality you mention.

您还可以查看这个包含您提到的功能的小库。

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 :)

它和这里其他的一些类似。希望帮助:)

#9


1  

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.

这个答案是基于Mark建议的方法。创建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.

这显示了如何使用它——这是驱动首选项显示的“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>

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- encryptioncompatibility/,尽管密钥和初始化向量的值不同。

#10


1  

First of all I think User's data shouldn't be stored on phone, and if it is must to store data somewhere on the phone it should be encrypted with in the apps private data. Security of users credentials should be the priority of the application.

首先,我认为用户的数据不应该存储在手机上,如果用户的数据必须存储在手机上的某个地方,就应该在应用程序的私有数据中加密。用户凭据的安全性应该是应用程序的优先级。

The sensitive data should be stored securely or not at all. In the event of a lost device or malware infection, data stored insecurely can be compromised.

敏感数据应该被安全地存储或根本不存储。在设备丢失或恶意软件感染的情况下,不安全存储的数据可能会被泄露。

#11


1  

This is a supplemental answer for those arriving here based on the question title (like I did) and don't need to deal with the security issues related to saving passwords.

这是基于问题标题(如我所做的)的补充回答,不需要处理与保存密码相关的安全问题。

How to use Shared Preferences

User settings are generally saved locally in Android using SharedPreferences with a key-value pair. You use the String key to save or look up the associated value.

用户设置通常使用带有键值对的SharedPreferences在Android中本地保存。使用字符串键保存或查找关联的值。

Write to Shared Preferences

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Use apply() instead of commit() to save in the background rather than immediately.

使用apply()代替commit()在后台保存,而不是立即保存。

Read from Shared Preferences

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

The default value is used if the key isn't found.

如果没有找到密钥,则使用默认值。

Notes

  • Rather than using a local key String in multiple places like I did above, it would be better to use a constant in a single location. You could use something like this at the top of your settings activity:

    与其像上面那样在多个地方使用本地键字符串,不如在单个位置使用常量。您可以在设置活动的顶部使用如下内容:

    final static String PREF_MY_INT_KEY = "myInt";
    
  • I used an int in my example, but you can also use putString(), putBoolean(), getString(), getBoolean(), etc.

    我在示例中使用了int,但您也可以使用putString()、putBoolean()、getString()、getBoolean()等。

  • See the documentation for more details.
  • 有关更多细节,请参阅文档。
  • There are multiple ways to get SharedPreferences. See this answer for what to look out for.
  • 有多种方法可以获得共享的首选项。看看这个问题的答案。

#12


-2  

you need to use the sqlite, security apit to store the passwords. here is best example, which stores passwords, -- passwordsafe. here is link for the source and explanation -- http://code.google.com/p/android-passwordsafe/

您需要使用sqlite、security apit来存储密码。下面是保存密码的最佳示例——passwordsafe。这里是源和解释的链接——http://code.google.com/p/android-passwordsafe/

#13


-2  

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.

共享首选项是存储应用程序数据的最简单方法。但是任何人都可以通过application manager清除我们共享的首选项数据。所以我认为这对我们的申请来说是不安全的。

#1


221  

In general SharedPreferences are your best bet for storing preferences, so in general I'd recommend that approach for saving application and user settings.

一般来说,SharedPreferences是存储首选项的最佳选择,因此通常我建议使用这种方法来保存应用程序和用户设置。

The only area of concern here is what you're saving. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text. The Android architecture is such that your application's SharedPreferences are sandboxed to prevent other applications from being able to access the values so there's some security there, but physical access to a phone could potentially allow access to the values.

这里唯一值得关注的是你在储蓄什么。密码总是一件很棘手的事情,我特别担心把它们存储为明文。Android的体系结构是这样的:您的应用程序的SharedPreferences被沙箱化,以防止其他应用程序能够访问这些值,因此存在一些安全性,但是对电话的物理访问可能允许访问这些值。

If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth. Alternatively you may need to construct some sort of cryptographic store, though that's non-trivial. At the very least, make sure you're encrypting the password before writing it to disk.

如果可能的话,我将考虑修改服务器以使用协商过的令牌来提供访问,类似OAuth。或者,您可能需要构建某种类型的加密存储,尽管这并非易事。至少,在将密码写入磁盘之前,要确保对密码进行了加密。

#2


199  

I agree with Reto and fiXedd. Objectively speaking it doesn't make a lot of sense investing significant time and effort into encrypting passwords in SharedPreferences since any attacker that has access to your preferences file is fairly likely to also have access to your application's binary, and therefore the keys to unencrypt the password.

我同意Reto和fiXedd。客观地说,在SharedPreferences中花费大量时间和精力对密码进行加密没有多大意义,因为任何访问您的首选项文件的攻击者都很可能访问您的应用程序的二进制文件,因此也有可能访问密码的密钥。

However, that being said, there does seem to be a publicity initiative going on identifying mobile applications that store their passwords in cleartext in SharedPreferences and shining unfavorable light on those applications. See http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ and http://viaforensics.com/appwatchdog for some examples.

不过,话虽如此,似乎确实有一项宣传活动正在进行,即识别将密码以共享首选项的方式存储在cleartext中的移动应用程序,并对这些应用程序发出不利的信号。查看http://blogs.wsj.com/digits/2011/06/08/some-top-app - putdata -risk/和http://viasics.com/appwatchdog为您提供一些示例。

While we need more attention paid to security in general, I would argue that this sort of attention on this one particular issue doesn't actually significantly increase our overall security. However, perceptions being as they are, here's a solution to encrypt the data you place in SharedPreferences.

虽然我们需要更多地关注一般的安全问题,但我认为,这种对这一特定问题的关注实际上并没有显著增加我们的整体安全。但是,由于感知是真实的,这里有一个解决方案来加密您在SharedPreferences中放置的数据。

Simply wrap your own SharedPreferences object in this one, and any data you read/write will be automatically encrypted and decrypted. eg.

只需将您自己的SharedPreferences对象包装在这个对象中,您所读/写的任何数据都将被自动加密和解密。如。

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

Here's the code for the class:

下面是这个类的代码:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}

#3


28  

About the simplest way to store a single preference in an Android Activity is to do something like this:

在Android活动中存储单一偏好的最简单方法是这样做:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

If you're worried about the security of these then you could always encrypt the password before storing it.

如果您担心这些密码的安全性,那么您可以在存储密码之前对其进行加密。

#4


9  

Using the snippet provided by Richard, you can encrypt the password before saving it. The preferences API however doesn't provide an easy way to intercept the value and encrypt it - you can block it being saved via an OnPreferenceChange listener, and you theoretically could modify it through a preferenceChangeListener, but that results in an endless loop.

使用Richard提供的代码片段,您可以在保存密码之前对密码进行加密。不过,preferences API并没有提供一种简单的方法来拦截值并对其进行加密——您可以通过OnPreferenceChange侦听器阻止它被保存,并且理论上可以通过preferenceChangeListener对其进行修改,但这将导致无休止的循环。

I had earlier suggested adding a "hidden" preference in order to accomplish this. It's definitely not the best way. I'm going to present two other options that I consider to be more viable.

我之前曾建议增加一个“隐藏”的偏好来完成这个任务。这绝对不是最好的方法。我将提出另外两个我认为更可行的选择。

First, the simplest, is in a preferenceChangeListener, you can grab the entered value, encrypt it, and then save it to an alternative preferences file:

首先,最简单的是在preferenceChangeListener中,您可以获取输入的值,对其进行加密,然后将其保存到另一个首选项文件中:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

The second way, and the way I now prefer, is to create your own custom preference, extending EditTextPreference, @Override'ing the setText() and getText() methods, so that setText() encrypts the password, and getText() returns null.

第二种方式,也是我现在喜欢的方式,是创建您自己的自定义首选项,扩展EditTextPreference, @Override' setText()和getText()方法,以便setText()加密密码,getText()返回null。

#5


5  

I know this is a little bit of necromancy, but you should use the 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.

我知道这有点死角,但是你应该使用Android AccountManager。它的目的是为这个场景构建的。这有点麻烦,但它所做的一件事是,如果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是一个使用存储帐户凭据的示例。

#6


5  

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. 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. Then again, do you want to support rooted devices?

    MODE_PRIVATE方法通常被认为是安全的,如果您假设用户没有root用户该设备。您的数据以纯文本的形式存储在文件系统的某个部分中,该部分只能由原始程序访问。这使得在根设备上使用另一个应用程序获取密码变得很容易。再说一遍,你想支持有根设备吗?

  2. AES is still the best encryption you can do. 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?"

    AES仍然是最好的加密技术。如果您正在启动一个新实现(如果我发布这篇文章已经有一段时间了),请记住要查找这个。最大的问题是“如何处理加密密钥?”

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

那么,现在我们就到了“如何处理密钥”部分。这是最难的部分。拿到钥匙并没有那么糟糕。您可以使用一个密钥派生函数来获取一些密码并使其成为一个非常安全的密钥。你会遇到这样的问题:“你用pkfdfdf2做了多少次传递?”但那是另一个话题

  1. Ideally, you store the AES key off the device. You have to figure out a good way to retrieve the key from the server safely, reliably, and securely though

    理想情况下,将AES密钥存储在设备上。您必须找到一种从服务器安全地、可靠地、安全地检索密钥的好方法。

  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.

    您有某种登录序列(甚至是远程访问的原始登录序列)。您可以在相同的密码上运行两次密钥生成器。它的工作原理是使用新的salt和新的安全初始化向量两次派生密钥。您将其中一个生成的密码存储在设备上,并使用第二个密码作为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. This is recommended by a recent-ish Android blog post. Not incredibly secure, but way better for some people over plain text
  2. 使用“一般安全”方法,使用AES加密数据,并将密钥存储在MODE_PRIVATE中。这是由Android博客推荐的。不是很安全,但对一些人来说,这比纯文本要好得多。

You can do a lot of variations of these. For example, instead of a full login sequence, you can do a quick PIN (derived). The quick PIN might not be as secure as a full login sequence, but it's many times more secure than plain text

你可以做很多变化。例如,您可以使用一个快速大头针(派生的)代替完整的登录序列。快速大头针可能不如完整的登录顺序安全,但它比纯文本安全得多

#7


5  

I'll throw my hat into the ring just to talk about securing passwords in general on Android. On Android, the device binary should be considered compromised - this is the same for any end application which is in direct user control. Conceptually, a hacker could use the necessary access to the binary to decompile it and root out your encrypted passwords and etc.

我将把我的帽子扔进戒指,只是为了讨论如何在Android上保护密码。在Android上,设备二进制文件应该被认为是有害的——这对于任何直接由用户控制的终端应用程序都是一样的。从概念上讲,黑客可以使用对二进制文件的必要访问来对其进行分解,并将您的加密密码等删除。

As such there's two suggestions I'd like to throw out there if security is a major concern for you:

因此,如果安全是你主要关心的问题,我想提出两个建议:

1) Don't store the actual password. Store a granted access token and use the access token and the signature of the phone to authenticate the session server-side. The benefit to this is that you can make the token have a limited duration, you're not compromising the original password and you have a good signature that you can use to correlate to traffic later (to for instance check for intrusion attempts and invalidate the token rendering it useless).

1)不要存储实际的密码。存储已授予的访问令牌,并使用访问令牌和电话的签名对会话服务器端进行身份验证。这样做的好处是,您可以使令牌具有有限的持续时间,您不会损害原始密码,并且您有一个良好的签名,您可以在以后使用它来关联到流量(例如检查入侵尝试并使令令牌失效)。

2) Utilize 2 factor authentication. This may be more annoying and intrusive but for some compliance situations unavoidable.

2)利用2因素认证。这可能会更烦人、更烦人,但对于某些合规情况来说,这是不可避免的。

#8


2  

You can also check out this little lib, containing the functionality you mention.

您还可以查看这个包含您提到的功能的小库。

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 :)

它和这里其他的一些类似。希望帮助:)

#9


1  

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.

这个答案是基于Mark建议的方法。创建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.

这显示了如何使用它——这是驱动首选项显示的“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>

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- encryptioncompatibility/,尽管密钥和初始化向量的值不同。

#10


1  

First of all I think User's data shouldn't be stored on phone, and if it is must to store data somewhere on the phone it should be encrypted with in the apps private data. Security of users credentials should be the priority of the application.

首先,我认为用户的数据不应该存储在手机上,如果用户的数据必须存储在手机上的某个地方,就应该在应用程序的私有数据中加密。用户凭据的安全性应该是应用程序的优先级。

The sensitive data should be stored securely or not at all. In the event of a lost device or malware infection, data stored insecurely can be compromised.

敏感数据应该被安全地存储或根本不存储。在设备丢失或恶意软件感染的情况下,不安全存储的数据可能会被泄露。

#11


1  

This is a supplemental answer for those arriving here based on the question title (like I did) and don't need to deal with the security issues related to saving passwords.

这是基于问题标题(如我所做的)的补充回答,不需要处理与保存密码相关的安全问题。

How to use Shared Preferences

User settings are generally saved locally in Android using SharedPreferences with a key-value pair. You use the String key to save or look up the associated value.

用户设置通常使用带有键值对的SharedPreferences在Android中本地保存。使用字符串键保存或查找关联的值。

Write to Shared Preferences

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Use apply() instead of commit() to save in the background rather than immediately.

使用apply()代替commit()在后台保存,而不是立即保存。

Read from Shared Preferences

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

The default value is used if the key isn't found.

如果没有找到密钥,则使用默认值。

Notes

  • Rather than using a local key String in multiple places like I did above, it would be better to use a constant in a single location. You could use something like this at the top of your settings activity:

    与其像上面那样在多个地方使用本地键字符串,不如在单个位置使用常量。您可以在设置活动的顶部使用如下内容:

    final static String PREF_MY_INT_KEY = "myInt";
    
  • I used an int in my example, but you can also use putString(), putBoolean(), getString(), getBoolean(), etc.

    我在示例中使用了int,但您也可以使用putString()、putBoolean()、getString()、getBoolean()等。

  • See the documentation for more details.
  • 有关更多细节,请参阅文档。
  • There are multiple ways to get SharedPreferences. See this answer for what to look out for.
  • 有多种方法可以获得共享的首选项。看看这个问题的答案。

#12


-2  

you need to use the sqlite, security apit to store the passwords. here is best example, which stores passwords, -- passwordsafe. here is link for the source and explanation -- http://code.google.com/p/android-passwordsafe/

您需要使用sqlite、security apit来存储密码。下面是保存密码的最佳示例——passwordsafe。这里是源和解释的链接——http://code.google.com/p/android-passwordsafe/

#13


-2  

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.

共享首选项是存储应用程序数据的最简单方法。但是任何人都可以通过application manager清除我们共享的首选项数据。所以我认为这对我们的申请来说是不安全的。