【问题标题】:How to handle auth0 403 error without adding specific code everywhere (Retrofit/okhttp/RxAndroid)如何在不到处添加特定代码的情况下处理 auth0 403 错误(Retrofit/okhttp/RxAndroid)
【发布时间】:2015-08-08 07:07:30
【问题描述】:

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

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

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

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

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

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

任何关于如何实现这一点的指针将不胜感激。

--

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

【问题讨论】:

    标签: android retrofit rx-java okhttp auth0


    【解决方案1】:

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

    为了测试,我编写了一个小型网络服务器,用于检查 TestHeader 是否存在 failsucceed,如果 fail 则返回 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();
                 }
             }
        )
    

    【讨论】:

      【解决方案2】:

      您可以在本地检查过期时间并通过检查令牌的exp 声明来相应地刷新,而不是仅在收到 403 响应后刷新令牌。例如,this example uses the same approach in 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;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-08-21
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-23
        相关资源
        最近更新 更多