Jetpack Compose导航下应用认证授权正确流程咨询
Compose 带认证功能应用的启动流程与导航校验方案
核心场景处理逻辑
针对你提到的三类场景,统一处理逻辑如下:
- 未授权用户:启动后直接跳转至登录页,同时清空回退栈,避免用户返回启动校验页面
- 已登录用户:启动时先校验本地登录状态,再发起网络请求验证会话有效性,确认有效后跳转首页;若会话无效,清除本地登录标记后跳转登录页
- 本地标记已登录但会话过期:分两种场景处理:
- 启动校验阶段:检测到会话过期则直接清除状态并跳转登录页
- 首页请求阶段:通过网络拦截器捕获401状态码,清除状态后强制跳转登录页
Android 12+ 原生SplashScreen下的校验位置
Android 12及以上无法禁用原生启动页,因此不能在自定义Splash页执行校验逻辑。正确做法是:
将一个仅负责校验的过渡页(AuthCheckScreen)设为导航的起始目标,这个页面只做两件事:展示加载态、执行登录状态校验与会话验证,完成后直接跳转目标页面,不会停留在该页面。
具体实现步骤
1. 本地登录状态管理
用DataStore(推荐)或SharedPreferences存储核心登录状态:
- 是否已登录的布尔标记
- 服务器返回的会话token
2. 启动校验页面实现
创建AuthCheckScreen,在LaunchedEffect中执行异步校验逻辑:
@Composable fun AuthCheckScreen(navController: NavHostController) { val context = LocalContext.current val dataStore = remember { context.createDataStore(name = "auth_prefs") } val isLoggedIn by dataStore.data .map { it[PreferencesKeys.IS_LOGGED_IN] ?: false } .collectAsState(initial = false) val token by dataStore.data .map { it[PreferencesKeys.TOKEN] ?: "" } .collectAsState(initial = "") LaunchedEffect(Unit) { // 先判断本地登录状态 if (!isLoggedIn) { navController.navigate("login") { popUpTo("auth_check") { inclusive = true } } return@LaunchedEffect } // 发起会话有效性校验请求 runCatching { apiService.validateSession(token) }.onSuccess { // 会话有效,跳转首页并清空回退栈 navController.navigate("home") { popUpTo("auth_check") { inclusive = true } } }.onFailure { // 会话过期或网络异常,清除本地状态后跳登录 dataStore.edit { it[PreferencesKeys.IS_LOGGED_IN] = false it[PreferencesKeys.TOKEN] = "" } navController.navigate("login") { popUpTo("auth_check") { inclusive = true } } } } // 展示加载态,避免原生启动页结束后出现空白 Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } }
3. 导航图配置
将auth_check设为起始目标,确保启动后先进入校验流程:
@Composable fun AppNavHost() { val navController = rememberNavController() NavHost(navController = navController, startDestination = "auth_check") { composable("auth_check") { AuthCheckScreen(navController = navController) } composable("login") { LoginScreen(navController = navController) } composable("home") { HomeScreen(navController = navController) } } }
4. 首页会话过期处理
在OkHttp拦截器中统一捕获401状态码,处理会话过期:
class AuthInterceptor(private val dataStore: DataStore<Preferences>, private val navController: NavHostController) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val response = chain.proceed(request) if (response.code == 401) { // 清除本地登录状态 CoroutineScope(Dispatchers.IO).launch { dataStore.edit { it[PreferencesKeys.IS_LOGGED_IN] = false it[PreferencesKeys.TOKEN] = "" } // 切换到主线程导航,清空回退栈 withContext(Dispatchers.Main) { navController.navigate("login") { popUpTo("home") { inclusive = true } launchSingleTop = true } } } } return response } }
注意事项
- 导航时务必使用
popUpTo清空回退栈,避免用户通过返回键回到已失效的页面 - 会话校验请求要处理网络异常,比如无网络时可先进入首页,后续请求再提示用户
- 本地状态修改后要确保UI及时响应,推荐使用DataStore的Flow监听状态变化
内容的提问来源于stack exchange,提问作者Sunbey13
相关产品推荐
相关产品推荐

