You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose导航下应用认证授权正确流程咨询

Compose 带认证功能应用的启动流程与导航校验方案

核心场景处理逻辑

针对你提到的三类场景,统一处理逻辑如下:

  • 未授权用户:启动后直接跳转至登录页,同时清空回退栈,避免用户返回启动校验页面
  • 已登录用户:启动时先校验本地登录状态,再发起网络请求验证会话有效性,确认有效后跳转首页;若会话无效,清除本地登录标记后跳转登录页
  • 本地标记已登录但会话过期:分两种场景处理:
    1. 启动校验阶段:检测到会话过期则直接清除状态并跳转登录页
    2. 首页请求阶段:通过网络拦截器捕获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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:20:04