简体   繁体   English

如何在不添加特定代码的情况下处理auth0 403错误(Retrofit / okhttp / RxAndroid)

[英]How to handle auth0 403 error without adding specific code everywhere (Retrofit/okhttp/RxAndroid)

I am using Auth0, which gives me a JWT (json web token) and a refreshtoken. 我正在使用Auth0,它给了我一个JWT(json web令牌)和一个refreshtoken。 I use this JWT in the http headers to communicate with my backend. 我在http标头中使用此JWT与我的后端进行通信。

It could happen, that the server gives me a 403 , when it decides that the JWT has expired. 当它确定JWT已经过期时,服务器可能会给我一个403 In this event, I can ask Auth0 to issue me a new JWT, using the refreshtoken. 在这种情况下,我可以让Auth0使用refreshtoken向我发出一个新的JWT。 It means I call the Auth0 backend, pass it the refreshtoken, and it gives me a new JWT, which I can then use in my requests. 这意味着我调用Auth0后端,将它传递给refreshtoken,它给了我一个新的JWT,然后我可以在我的请求中使用它。

My question is, how can I efficiently write this behaviour in all my networking code? 我的问题是,如何在我的所有网络代码中有效地编写此行为? I will have a couple of endpoints to talk to, and they all might return the 403. 我将有几个端点可以交谈,他们都可以返回403。

I am thinking I should first make an interceptor that adds the JWT to all requests. 我想我应该首先制作一个拦截器,将JWT添加到所有请求中。

Then there should be behaviour that detects the 403, quietly does a networkcall to Auth0, retrieving the new JWT. 然后应该有检测403的行为,静静地对Auth0进行网络调用,检索新的JWT。 Then the original request should be tried again, with the new JWT in its headers. 然后应该再次尝试原始请求,并在其标题中添加新的JWT。

So I would prefer to have this 403 handling somewhere invisible to my other code, and definitely not have to rewrite it everywhere. 所以我更希望将403处理到我的其他代码看不到的地方,并且绝对不必在任何地方重写它。

Any pointers on how to achieve this will be appreciated. 任何关于如何实现这一点的指示将不胜感激。

-- -

To be clear, I am basically looking for pointers on how to achieve this using RxAndroid Observables. 要清楚,我基本上是在寻找如何使用RxAndroid Observables实现这一目标的指针。 When a certain Observable finds the 403, it should 'inject' a new network call. 当某个Observable找到403时,它应该“注入”一个新的网络呼叫。

I solved this issue by writing an Interceptor for OkHttp . 我通过为OkHttp编写一个Interceptor来解决这个问题。 It checks the statuscode of the network call. 它检查网络呼叫的状态代码。 If it's a 403, call Auth0 servers and request a new id_token. 如果是403,请调用Auth0服务器并请求新的id_token。 Then use this token in a new version of the original request. 然后在原始请求的新版本中使用此令牌。

To test, I wrote a little webserver that checks the TestHeader for fail or succeed and returns a 403 if it's fail . 为了测试,我编写了一个小的Web服务器来检查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;
        }
    }
}

Then I attach this Interceptor to an OkHttpClient which I plug into the Retrofit adapter: 然后我将这个Interceptor附加到一个OkHttpClient,我将其插入Retrofit适配器:

// 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();
             }
         }
    )

Instead of refreshing tokens only after receiving a 403 response, you could check the expiration time locally and refresh accordingly by checking the token's exp claim. 只有在收到403响应后才能刷新令牌,您可以在本地检查到期时间,并通过检查令牌的exp声明进行相应的刷新。 For example, this example uses the same approach in Angular . 例如, 此示例在Angular中使用相同的方法 It's not specific to Android, but the idea is the same: 它并非特定于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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM