拦截调用以刷新令牌GRPC

时间:2018-08-14 11:58:27

标签: android kotlin interceptor grpc

我在项目中使用了带有原型的GRPC,并且我具有KEY和AUTHORITY令牌来访问服务器API。 因此,我需要使用我的AUTHORITY更新KEY。 我正在建立这样的频道:

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
    }
}

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

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

1)向服务器发送请求;

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

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

2 个答案:

答案 0 :(得分:0)

如果您要这样做,我认为您必须按照您所描述的那样在应用程序级别进行处理。这是因为gRPC不了解您的应用程序级别令牌。

  1. 进行RPC
  2. 请注意,由于令牌过期,RPC失败
  3. 调用刷新令牌的代码
  4. 重复

您指的是什么身份验证者?

答案 1 :(得分:0)

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

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.