首页 > 解决方案 > 有没有办法将 EncryptedSharedPreference 与 PreferenceScreen 集成?

问题描述

我是android开发的新手。目前,我想加密一个名为 Shared Preference 的自定义并与 PreferenceScreen 集成但未能这样做。我正在使用依赖项:

  1. androidx.security:security-crypto:1.0.0-alpha02 [EncryptedSharedPreference]
  2. androidx.preference:preference:1.1.0 [PreferenceScreen]

我曾尝试研究有关这两个功能集成的相关信息,但未找到相关信息。

根据我的测试,我有一个现有的加密共享首选项并测试了以下 API:

getPreferenceManager().setSharedPreferencesName("MyShared"); //MyShared Is custom named preference.

但它最终以简单的价值保存了偏好。

我的问题:

  1. 在当前阶段是否可以将这两个功能集成在一起?
  2. PreferenceScreen 是否提供我不知道的加密功能?
  3. 如果我坚持使用 EncryptedSharedPreference,我创建一个看起来像偏好屏幕的自定义活动会更好吗?

标签: javasharedpreferencespreferencescreenandroid-jetpack-security

解决方案


A1:是的,这是可能的。

A3:您可以通过以下方式利用系统提供的设置。

由于 Kotlin 在一段时间内是首选的一等公民,现在我将在 Kotlin 中展示它,@Rikka 在另一个答案中有一个 Java 版本。对于 Kotlin 来说,诀窍是仍然设置preferencesDataSource,它是这样的:

class SettingsFragment : PreferenceFragmentCompat() {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        preferenceManager.preferenceDataStore =
            EncryptedPreferenceDataStore.getInstance(requireContext())

        // Load the preferences from an XML resource
        setPreferencesFromResource(R.xml.preferences, rootKey)
    }

Kotlin 版本EncryptedPreferenceDataStore:我使用also单例的关键字,类似于 Google 源代码相关的单例 Room 示例,带有 Kotlin 中的参数

class EncryptedPreferenceDataStore private constructor(context: Context) : PreferenceDataStore() {
    companion object {
        private const val SHARED_PREFERENCES_NAME = "secret_shared_preferences"

        @Volatile private var INSTANCE: EncryptedPreferenceDataStore? = null

        fun getInstance(context: Context): EncryptedPreferenceDataStore =
            INSTANCE ?: synchronized(this) {
                INSTANCE ?: EncryptedPreferenceDataStore(context).also { INSTANCE = it }
            }
    }

    private var mSharedPreferences: SharedPreferences
    private lateinit var mContext: Context

    init {
        try {
            mContext = context
            val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build()

            mSharedPreferences = EncryptedSharedPreferences.create(
                context,
                SHARED_PREFERENCES_NAME,
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            )
        } catch (e: Exception) {
            // Fallback, default mode is Context.MODE_PRIVATE!
            mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
        }
    }

    override fun putString(key: String, value: String?) {
        mSharedPreferences.edit().putString(key, value).apply()
    }

    override fun putStringSet(key: String, values: Set<String>?) {
        mSharedPreferences.edit().putStringSet(key, values).apply()
    }

    override fun putInt(key: String, value: Int) {
        mSharedPreferences.edit().putInt(key, value).apply()
    }

    override fun putLong(key: String, value: Long) {
        mSharedPreferences.edit().putLong(key, value).apply()
    }

    override fun putFloat(key: String, value: Float) {
        mSharedPreferences.edit().putFloat(key, value).apply()
    }

    override fun putBoolean(key: String, value: Boolean) {
        mSharedPreferences.edit().putBoolean(key, value).apply()
    }

    override fun getString(key: String, defValue: String?): String? {
        return mSharedPreferences.getString(key, defValue)
    }

    override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? {
        return mSharedPreferences.getStringSet(key, defValues)
    }

    override fun getInt(key: String, defValue: Int): Int {
        return mSharedPreferences.getInt(key, defValue)
    }

    override fun getLong(key: String, defValue: Long): Long {
        return mSharedPreferences.getLong(key, defValue)
    }

    override fun getFloat(key: String, defValue: Float): Float {
        return mSharedPreferences.getFloat(key, defValue)
    }

    override fun getBoolean(key: String, defValue: Boolean): Boolean {
        return mSharedPreferences.getBoolean(key, defValue)
    }
}

也许通过双重同步检查可能会更加线程安全?


推荐阅读