处理MVI Kotlin应用程序中的安全令牌故障



我在Kotlin中构建了一个自定义应用程序,使用单个活动跟踪MVI。

ViewModel的基本模式->存储库->重新设置API

用户登录并获得一个令牌,然后在所有后续API调用中使用该令牌。最终,此令牌将过期或可能在后端过期。

我正试图弄清楚如何在低级别以干净的方式处理过期的令牌,而不是用处理过期令牌的逻辑污染我的所有片段。

如果令牌过期,我希望用户进入登录页面/片段。

以下是我认为可以做到的方法。首先,定义一个接口,该接口提供注销合约。例如:

interface LogOutOwner {
fun logout(): Observable<Unit>
}

实现这个接口,在这里我使用拦截器作为实现,因为它提供了足够的信息来推断是否需要注销:

class ErrorInterceptor : Interceptor, LogOutOwner {
private val publishLogOutSubject: PublishSubject<Unit> = PublishSubject.create()
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.code == 401) { // check if a logout is needed
publishLogOutSubject.onNext(Unit)
}
return response
}
override fun logout(): Observable<Unit> {
return publishLogOutSubject
}
}

为了使其工作,您的拦截器和LogOutOwner应该是singleton,这样它们就可以处理您的所有网络请求。实现这一点的方法之一是使用依赖注入框架。作为一个例子,我展示了手动依赖注入:

object Injection {
private val errorInterceptor = ErrorInterceptor()
fun provideLogOutOwner(): LogOutOwner = errorInterceptor
private fun provideOkHttpClient(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(errorInterceptor)
return httpClient.build()
}
fun provideRetrofit() = Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create())
.client(provideOkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}

由于Kotlin对象只有一个实例,因此errorInterceptorInjection的范围内也只实例化一次。最后,现在您需要订阅LogOutOwner来侦听注销事件。由于你只有一个活动,你可以在那里订阅并打开你需要的任何片段。但是,通过这样做,您最终需要处理注销订阅,并且每当您想处理不同活动或片段的注销时,您都需要实现相同的取消逻辑。为了使其更加通用,在不需要在需要处理注销时引入相同的样板的情况下,请考虑使用生命周期感知组件。这里有一个例子:

class LogOutObserver(
private val logOutOwner: LogOutOwner,
private val logoutAction: () -> Unit
) :
LifecycleObserver {
private var disposable: Disposable? = null
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
disposable = logOutOwner.logout()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { logoutAction.invoke() }
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
disposable?.dispose()
}
}

当您需要处理注销时,只需使用如下观察器:

lifecycle.addObserver(LogOutObserver(Injection.provideLogOutOwner()) {
// do logout
})

因此,通过引入上面的所有代码,我们减少了活动或片段应该实现的逻辑量。基本上,活动或片段现在只需要关心导航逻辑,而不必担心注销或任何生命周期事件的原因。我在这里使用了RxJava,但如果需要的话,用Kotlin Coroutines实现它并不难。

附言:如果您需要它与多个片段/活动一起使用,您需要更改LogOutObserver,使其在onResume()onPause()中订阅/取消订阅,以防止同时从多个活动/片段调用注销操作。

最新更新