Android Jetpack Compose中登录信息(JWT等)存储方案咨询
登录信息的存储必要性与实现方案
一、必须存储登录信息
登录信息(用户名、JWT Token)必须持久化存储,核心原因:
- 避免用户每次启动App或切换后台后重复登录,提升使用体验
- JWT是接口鉴权的核心凭证,持久化后App启动即可获取,无需重新请求
- 跨页面、跨会话保持登录状态,支撑全局业务逻辑
二、可行的实现方案
1. 加密本地持久化(首选)
敏感信息必须加密存储,推荐使用Jetpack的Encrypted DataStore(替代SharedPreferences),若需更复杂的数据结构可使用Room。
代码示例(Encrypted DataStore)
先添加依赖(build.gradle.kts):
dependencies { implementation("androidx.datastore:datastore-preferences:1.0.0") implementation("androidx.datastore:datastore-preferences-core:1.0.0") implementation("androidx.security:security-crypto:1.1.0-alpha05") }
封装AuthPreferences工具类:
import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import android.content.Context object AuthPreferences { private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "auth_prefs") private val USER_NAME = stringPreferencesKey("user_name") private val JWT_TOKEN = stringPreferencesKey("jwt_token") // 保存登录信息 suspend fun saveAuthInfo(context: Context, userName: String, token: String) { context.dataStore.edit { prefs -> prefs[USER_NAME] = userName prefs[JWT_TOKEN] = token } } // 监听登录信息变化 fun getAuthInfoFlow(context: Context): Flow<Pair<String?, String?>> { return context.dataStore.data.map { prefs -> Pair(prefs[USER_NAME], prefs[JWT_TOKEN]) } } // 清除登录信息(登出) suspend fun clearAuthInfo(context: Context) { context.dataStore.edit { it.clear() } } }
使用方式:
- 登录成功后调用
saveAuthInfo存储信息 - App启动时通过
getAuthInfoFlow观察状态,判断是否自动登录 - 接口请求时从Flow中获取Token,添加到请求头
2. 共享ViewModel(内存状态补充)
你尝试的共享ViewModel方案是可行的,但仅能在App运行期间维持状态,重启后会丢失,因此必须配合本地持久化使用,用于全局同步登录状态。
代码示例:
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import android.content.Context class AuthViewModel : ViewModel() { sealed class AuthState { object Unauthenticated : AuthState() data class Authenticated(val userName: String, val token: String) : AuthState() } private val _authState = MutableStateFlow<AuthState>(AuthState.Unauthenticated) val authState: StateFlow<AuthState> = _authState // 登录:保存到本地+更新内存状态 fun login(context: Context, userName: String, token: String) { viewModelScope.launch { AuthPreferences.saveAuthInfo(context, userName, token) _authState.value = AuthState.Authenticated(userName, token) } } // 加载本地存储的登录信息,初始化状态 fun loadSavedAuth(context: Context) { viewModelScope.launch { AuthPreferences.getAuthInfoFlow(context) .collect { (userName, token) -> if (userName != null && token != null) { _authState.value = AuthState.Authenticated(userName, token) } else { _authState.value = AuthState.Unauthenticated } } } } // 登出:清除本地+更新内存状态 fun logout(context: Context) { viewModelScope.launch { AuthPreferences.clearAuthInfo(context) _authState.value = AuthState.Unauthenticated } } }
使用方式:
在Compose中通过viewModel<AuthViewModel>()获取实例,观察authState控制页面导航:
val authViewModel = viewModel<AuthViewModel>() val authState by authViewModel.authState.collectAsState() LaunchedEffect(Unit) { authViewModel.loadSavedAuth(context) } when (authState) { is AuthViewModel.AuthState.Unauthenticated -> LoginScreen() is AuthViewModel.AuthState.Authenticated -> HomeScreen() }
3. 为什么不推荐用Navigation传递登录信息
Navigation组件的参数传递适合少量、临时的数据,登录信息是全局核心状态:
- 逐个页面传递会导致代码冗余,维护成本高
- NavTypes不支持直接传递自定义对象,即使序列化后传递,也无法解决App重启后状态丢失的问题
- 全局状态更适合用持久化+共享ViewModel的组合方案管理
内容的提问来源于stack exchange,提问作者lmr
相关产品推荐
相关产品推荐

