13

我正在使用 Auth0,它给了我一个 JWT(json Web 令牌)和一个 refreshtoken。我在 http 标头中使用此 JWT 与我的后端通信。

403当服务器确定 JWT 已过期时,可能会发生这种情况。在这种情况下,我可以要求 Auth0 使用 refreshtoken 向我发出一个新的 JWT。这意味着我调用 Auth0 后端,将 refreshtoken 传递给它,它给了我一个新的 JWT,然后我可以在我的请求中使用它。

我的问题是,如何在我的所有网络代码中有效地编写这种行为?我将有几个端点要与之交谈,它们都可能返回 403。

我想我应该首先制作一个拦截器,将 JWT 添加到所有请求中。

然后应该有检测到 403 的行为,悄悄地对 Auth0 进行网络调用,检索新的 JWT。然后应该再次尝试原始请求,并在其标头中使用新的 JWT。

所以我宁愿让这个 403 处理在我的其他代码不可见的地方,并且绝对不必在任何地方重写它。

任何有关如何实现这一目标的指示都将不胜感激。

--

需要明确的是,我基本上是在寻找有关如何使用 RxAndroid Observables 实现这一目标的指针。当某个 Observable 找到 403 时,它应该“注入”一个新的网络调用。

4

2 回答 2

21

我通过编写Interceptorfor解决了这个问题OkHttp。它检查网络调用的状态码。如果是 403,则调用 Auth0 服务器并请求新的 id_token。然后在原始请求的新版本中使用此令牌。

为了进行测试,我编写了一个小型网络服务器来检查TestHeader是否失败,如果失败则返回403

public class AuthenticationInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request authenticationRequest = originalRequest.newBuilder()
                .header("TestHeader", "fail")
                .build();

        Response origResponse = chain.proceed(authenticationRequest);

        // server should give us a 403, since the header contains 'fail'
        if (origResponse.code() == 403) {
            String refreshToken = "abcd"; // you got this from Auth0 when logging in

            // start a new synchronous network call to Auth0
            String newIdToken = fetchNewIdTokenFromAuth0(refreshToken);

            // make a new request with the new id token
            Request newAuthenticationRequest = originalRequest.newBuilder()
                    .header("TestHeader", "succeed")
                    .build();

            // try again
            Response newResponse = chain.proceed(newAuthenticationRequest);

            // hopefully we now have a status of 200
            return newResponse;
        } else {
            return origResponse;
        }
    }
}

然后我将此拦截器附加到我插入改造适配器的 OkHttpClient:

// add the interceptor to an OkHttpClient

public static OkHttpClient getAuthenticatingHttpClient() {
    if (sAuthenticatingHttpClient == null) {
        sAuthenticatingHttpClient = new OkHttpClient();
        sAuthenticatingHttpClient.interceptors().add(new AuthenticationInterceptor());
    }
    return sAuthenticatingHttpClient;
}

// use the OkHttpClient in a Retrofit adapter

mTestRestAdapter = new RestAdapter.Builder()
    .setClient(new OkClient(Network.getAuthenticatingHttpClient()))
    .setEndpoint("http://ip_of_server:port")
    .setLogLevel(RestAdapter.LogLevel.FULL)
    .build();

// call the Retrofit method on buttonclick

ViewObservable.clicks(testNetworkButton)
    .map(new Func1<OnClickEvent, Object>() {
             @Override
             public Object call(OnClickEvent onClickEvent) {
                 return mTestRestAdapter.fetchTestResponse();
             }
         }
    )
于 2015-05-27T11:07:23.040 回答
2

您可以在本地检查过期时间并通过检查令牌的声明进行相应刷新,而不是仅在收到 403 响应后刷新令牌exp。例如,此示例在 Angular 中使用相同的方法。它不是特定于 Android 的,但想法是一样的:

jwtInterceptorProvider.tokenGetter = function(store, jwtHelper, auth) {
  var idToken = store.get('token');
  var refreshToken = store.get('refreshToken');
  if (!idToken || !refreshToken) {
    return null;
  }
  // If token has expired, refresh it and return the new token
  if (jwtHelper.isTokenExpired(idToken)) {
    return auth.refreshIdToken(refreshToken).then(function(idToken) {
      store.set('token', idToken);
      return idToken;
    });
  // If not expired, return the token directly
  } else {
    return idToken;
  }
}
于 2015-05-26T14:45:30.140 回答