preface

Generally, after the user logs in, the application saves the information related to the token for subsequent requests. This involves the validity period of the token. Each time you open an application, the local device checks whether the token is valid. If the token is invalid, you need to obtain a new token. But there are some problems with local authentication:

  1. The user’s mobile phone time is adjusted
  2. After entering the application for a period of time, the token expires

Therefore, you need to determine the validity period of the token based on the returned request. The following two solutions are provided.

Authenticator

Using the Authnenticator interface provided by OkHttp, OkHttp will re-request if it returns 401, but note that this interface will only be invoked if the status code returned by HTTP is 401. This method can be used if the backend interface is designed in accordance with the specification.

public class TokenAuthenticator implements Authenticator { @Override public Request authenticate(Route route, To Response the Response) throws IOException {/ / to get new token, using a synchronous request String newToken = service. The refreshToken. The execute (). The body ();  // save the newToken config.savetoken (newToken); return response.request().newBuilder() .addHeader("Authorization",newToken) .build(); }}Copy the code

Of course, not all requests put the token inside the Header, such as the Query parameter, so we can do this:

public class TokenAuthenticator implements Authenticator { @Override public Request authenticate(Route route, Response Response) throws IOException {// to obtain a newToken, using the synchronous request method. String newToken = call.execute().body(); // save the newToken config.savetoken (newToken); Request originalRequest = response.request(); HttpUrl url = originalRequest.url() .newBuilder() .setQueryParameter("Token", newToken) .build(); return response.request().newBuilder() .url(url) .build(); }}Copy the code

Request originalRequest = response.request(); HttpUrl url = originalRequest.url();

Originalrequest.body (); originalRequest.body(); originalRequest.body(); Finally we set up the Authenticator for OkHttp

OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.authenticator(new TokenAuthenticator()); .Copy the code

However, not all interfaces are designed this way, if instead of returning the status code of 401, they return data of the following type:

{
	"message":"UNAUTHORIZED",
	"code":401}
	Copy the code

If this is the custom data format, then using an Authenticator will not work.

Interceptor

Use OkHttp’s interceptor Intercaptor

public class TokenInterceptor implements Interceptor { public static final Charset UTF_8 = Charset.forName("UTF-8"); @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Response originalResponse = chain.proceed(originalRequest); ResponseBody ResponseBody = originalResponse.body(); BufferedSource source = originalResponse.body().source(); source.request(Integer.MAX_VALUE); Buffer buffer = source.buffer(); Charset charset = UTF_8; MediaType contentType = responseBody.contentType(); if (contentType ! = null) { charset = contentType.charset(); } String bodyString = buffer.clone().readString(charset); / / if the token has expired if (token is expired) {String newToken = service. The refreshToken. The execute (). The body (); // save the newToken config.savetoken (newToken); // Add the Query parameter HttpUrl URL = chain.request().url().newBuilder().setQueryParameter("Token", newToken).build(); Request newRequest = chain.request().newBuilder() .url(url) .build(); / / added to the Header Request newRequest = originalRequest. NewBuilder (). The Header (" Authorization ", newToken). The build (); originalResponse.body().close(); return chain.proceed(newRequest); } return originalResponse; }Copy the code

Request originalRequest = chain.request(); Response originalResponse = chain.proceed(originalRequest);

Note that follow the documentation for ResponseBody

The response body can be consumed only once.

This class may be used to stream very large responses. For example, it is possible to use this class to read a response that is larger than the entire memory allocated to the current process. It can even stream a response larger than the total storage on the current device, which is a common requirement for video streaming applications.

Because this class does not buffer the full response in memory, the application may not re-read the bytes of the response. Use this one shot to read the entire response into memory with bytes() or string(). Or stream the response with either source(), byteStream(), or charStream().

The data returned by the request may be too large, so not all data is cached in memory and the application cannot read it

So when processing the returned data, you can use the method in the above code to process the returned data (as well as other methods), or to save trouble, Use response.body ().string(), but remember to rerequest return chain.procees(resquest) at the end

Request originalRequest = chain.request(); Response originalResponse = chain.proceed(originalRequest); ResponseBody ResponseBody = originalResponse.body(); // do something return chain.proceed(request);Copy the code

Also, set the Interceptor for OkHttp

OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.addInterceptor(new TokenInterceptor()); .Copy the code

conclusion