首页 > 解决方案 > 使用 Okhttp 的验证器(以及 RetroFit、RxJava 和 Dagger2)实现全局强制注销

问题描述

任务

我有一种情况,如果刷新令牌请求返回 401,我会通过从房间和内存中清除用户来强制注销。我想从 Authenticator 或 Interceptor 内部实现这一点,所以我不必检查 401 作为对每个调用的响应。

验证器

public Request authenticate(Route route, Response response) throws IOException {
//        if (responseCount(response) >= 3) {
//            return null;
//        }
//
        UserCredentials userCredentials = new
                UserCredentials().
                CreateRefreshTokenRequest("refresh_token",
                        requestHeaders.getAccessToken().getRefreshToken(),
                        oAuthClient.getClientId(),
                        oAuthClient.getClientSecret());
        Request.Builder builder = response.request().newBuilder();

        if (accountsServicesHolder.getAccountsServices() == null)
            return null;

        //this HAS TO BE a Synchronous process. The usual RXjava shenanigans can't be applied here.
        try {
           //Synchronous call to get new token
            AccessToken accessToken = accountsServicesHolder.
                    getAccountsServices().
                    refreshToken(userCredentials.toMap()).
                    blockingGet();


            //persist the token in the db
            //userDao.save()

            //persist the token in memory

            //send an updated version to the backend
            builder.header("Authorization", "Bearer " + accessToken.getAccessToken());
        } catch (Exception ex) {
            if (ex instanceof HttpException) { 
                return builder.
                        url(linksProvider.getProductionUrl().concat(logoutEndpoint)).
                        build(); //Redirecting this authenticator to a logout ws so that we do not get a 401 . Something goes wrong here 
            }
        }

        return builder.build();
    }

如您所见,如果我们没有在刷新令牌的基础上获得令牌,我会巧妙地将新 URL 插入到构建器中。这样我就不会得到一个 401 被冒泡到我最初打电话的地方。我不希望用户收到“401 You have not authorized”消息,而是显示一个 toast 说“Session Expired”

请求拦截器

@Override
public Response intercept(Chain chain) throws IOException {
    Request original = chain.request();
    Request.Builder builder = original.newBuilder().
            header("Authorization", "Bearer " + requestHeaders.getAccessToken().getAccessToken()).
            header("Accept-Language", requestHeaders.getLanguage()).
            method(original.method(), original.body());
    Request newRequest = builder.build();

    Response response = chain.proceed(newRequest);

    if (response.code() == 200
            && response.request().url().toString().contains("logout")) {

        forceUserOut();
    }
    return response;
}

forceUserOut 方法向系统广播已发生注销。由于我们已经针对 401 进行了保护,因此现在不会向用户显示任何错误。

问题

好的,现在我们来解决问题。我以为自己狡猾的地方可能毕竟没有那么聪明。身份验证器在调用注销 API 后运行额外的时间,该 API 在应该发生两次的事情之后运行。

据我了解,身份验证器应该刚刚执行了注销服务,而拦截器应该已经开始工作了。

但是,身份验证器执行以下操作

1) 尝试获取新的访问令牌

2)失败并调用注销API(调用Interceptor并广播注销)

3)失败并调用注销API(调用拦截器并广播注销)

4) 结束

我知道我可能会破坏 OkHttps 身份验证机制中的某些内容,但是我可以使用哪些其他方法来实现强制注销,而无需对所有 API 编写 401 检查?

对长长的问题深表歉意。

标签: androidoauth-2.0retrofit2okhttp3

解决方案


推荐阅读