android - 拦截调用以刷新 token GRPC

标签 android kotlin interceptor grpc

我在我的项目中使用 GRPC 和 proto,我有 KEY 和 AUTHORITY token 来访问服务器 API。 所以,我需要使用我的 AUTHORITY 更新 KEY。 我正在这样构建 channel :

OkHttpChannelBuilder.forAddress(host, port)
        .usePlaintext()
        .intercept(auth, logger)
        .build()

我的拦截器看起来像:

class AuthClientInterceptor(
    private val prefs: Preferences,
    private val keyApi: KeyApi) : ClientInterceptor {

    companion object {
        private const val ACCESS_TOKEN = "authorization"
    }

    override fun <ReqT : Any?, RespT : Any?> interceptCall(method: MethodDescriptor<ReqT, RespT>?,
                                                       callOptions: CallOptions?,
                                                       next: Channel): ClientCall<ReqT, RespT> {

        val call = next.newCall(method, callOptions)

        val callForwarding = object : ClientInterceptors.CheckedForwardingClientCall<ReqT, RespT>(call) {
            override fun checkedStart(responseListener: Listener<RespT>?, headers: Metadata) {

            synchronized(this@AuthClientInterceptor) {
                val keyCreated = prefs.getAccessKeyCreated()
                val keyExpires = prefs.getAccessKeyExpires()
                val currentTime = System.currentTimeMillis()
                if (currentTime < keyCreated || currentTime > keyExpires) {
                    keyApi.issueNewKey(prefs.getAuthority())
                        .map { it.data }
                        .doOnSuccess { prefs.setAccessKey(it.token) }
                        .doOnSuccess { prefs.setAccessKeyCreated(it.createdDate) }
                        .doOnSuccess { prefs.setAccessKeyExpires(it.expiresDate) }
                        .blockingGet()
                }
            }

            val keyData = Metadata.Key.of(ACCESS_TOKEN, Metadata.ASCII_STRING_MARSHALLER)
                if (headers[keyData] == null) {
                    headers.put(keyData, "Bearer ${prefs.getAccessKey()}")
                }
                call.start(responseListener, headers)
            }
        }
        return callForwarding
    }
}

如您所见,我只检查当前时间并将其与 token 创建日期和到期日期进行比较。

所以,我不喜欢那样。我想实现这个:

1) 向服务器发送请求;

2) 检查响应。如果这意味着我的 KEY 已过期,则同步刷新 key 并重复请求(如身份验证器)。

但我没有找到解决方案或任何有关使用 gRPC 实现此功能的有用信息。有人可以帮助我吗?

最佳答案

这是您可以使用的完整客户端拦截器类。

class Interceptor() : ClientInterceptor {

    override fun <ReqT : Any?, RespT : Any?> interceptCall(method: MethodDescriptor<ReqT, RespT>?, callOptions: CallOptions?, next: Channel?): ClientCall<ReqT, RespT> {

        return object : ClientCall<ReqT, RespT>() {

            var listener: Listener<RespT>? = null
            var metadata: Metadata? = null
            var message: ReqT? = null
            var request = 0
            var call: ClientCall<ReqT, RespT>? = null

            override fun start(responseListener: Listener<RespT>?, headers: Metadata?) {
                this.listener = responseListener
                this.metadata = headers
            }

            override fun sendMessage(message: ReqT) {
                assert(this.message == null)
                this.message = message
            }

            override fun request(numMessages: Int) {
                request += numMessages
                assert(this.message == null)
            }

            override fun isReady(): Boolean {
                return false
            }

            override fun halfClose() {

                startCall(object : ForwardingClientCallListener<RespT>() {

                    var delegate: Listener<RespT>? = null

                    override fun onReady() {
                        delegate = listener
                        super.onReady()
                    }

                    override fun delegate(): Listener<RespT> {
                        if (delegate == null) {
                            throw IllegalStateException()
                        }
                        return delegate!!
                    }

                    override fun onClose(status: Status?, trailers: Metadata?) {
                        if (delegate == null) {
                            super.onClose(status, trailers)
                            return
                        }
                        if (!needToRetry(status, trailers)) {
                            delegate = listener
                            super.onClose(status, trailers)
                            return
                        }
                        startCall(listener) // Only retry once
                    }

                    private fun needToRetry(status: Status?, trailers: Metadata?): Boolean {
                        if (status?.code?.toStatus() == UNAUTHENTICATED) {
                            Log.e("code", status?.code.toString())
                            return true
                        }
                        return false
                    }
                })
            }

            private fun startCall(listener: Listener<RespT>?) {
                call = next?.newCall(method, callOptions)
                val headers = Metadata()
                headers.merge(metadata)
                call?.start(listener, headers)
                assert(this.message != null)
                call?.request(request)
                call?.sendMessage(message)
                call?.halfClose()
            }

            override fun cancel(message: String?, cause: Throwable?) {
                if (call != null) {
                    call?.cancel(message, cause)
                }
                listener?.onClose(Status.CANCELLED.withDescription(message).withCause(cause), Metadata())
            }
        }
    }
}

它缓冲消息并重试,您可以在 needToRetry(status, trailers) 中添加您的逻辑

更多信息可以访问这个GitHub link.

关于android - 拦截调用以刷新 token GRPC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51840892/

相关文章:

android - 在 Android Jetpack Compose 中使用 State 时出现 java.lang.IllegalStateException

android - 更新到 WorkManager 1.0.0-alpha09 后编译错误

java - 如何在Spring拦截器中使用@ExceptionHandler?

android - Kotlin gradle.build ZipException 因为重复

java - AspectJ 拦截器不工作

java - jax-rs Apache CXF,拦截器之间传输数据

android - Android项目的选项卡+滑动应用程序中不同选项卡的不同菜单

java - 服务中的套接字 + 通知

Android Studio 模拟器没有运行我的应用

android - 将聊天服务器应用程序从 parse.com 移动到 google app engine