The token use of OkHttp

Posted by joking on Thu, 27 Jun 2019 23:58:34 +0200

Principle analysis

The general logic of token is that the login interface obtains a token and refresh_token for refreshing token.
In the next token request, all interfaces need to carry the token request so that the server can verify that the source of the request is legitimate.

This token is timeliness, the server will give him a timeliness, usually set to 2 hours, in this period of time,
The token is valid and can request success, and after that time, the token is invalid.
At this point, we need to use refresh_token to request a refresh of token's interface to get new token and refresh_token.
In this way, we can keep our token s valid and legitimate.

design idea

Because the token expiration is supposed to be invisible to users, we need to sneak in the background on android.
In other words, when requesting the interface, if we return the TOKEN expiration exception, we need to request the interface refresh immediately.
Then you re-request the interface with the new TOKEN, and you can imagine that the whole logic, if every interface needs to be written like this.
That means we'll have a lot of work, so it's not scientific, so we need to use an interceptor, which is very simple.

Next, I put in my code and explain it according to the code.

Code parsing

public class TokenInterceptor implements Interceptor {

    private static final Charset UTF8 = Charset.forName("UTF-8");

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //Step 1
        String token = (String) SPUtils.get(AppContext.getInstance(), SPUtils.TOKEN, "");

        if (!TextUtils.isEmpty(token) {

            HttpUrl.Builder authorizedUrlBuilder = request.url()
                    .newBuilder()
                    .scheme(request.url().scheme())
                    .host(request.url().host())
                    .addQueryParameter("access_token", token);

            request = request.newBuilder()
                    .method(request.method(), request.body())
                    .url(authorizedUrlBuilder.build())
                    .build();
        }
        //Step 2
        Response originalResponse = chain.proceed(request);
        StatusVo func = getResult(originalResponse);

        if (Status.TOKEN_Invalid.getCode() == func.getCode()) {
            //Step 3
            String newToken = getNewToken(loginBean);
            if (TextUtils.isEmpty(newToken)) {
                return originalResponse;
            } else {
                //Step 4
                HttpUrl.Builder newUrlBuilder = request.url()
                        .newBuilder()
                        .scheme(request.url().scheme())
                        .host(request.url().host())
                        .removeAllQueryParameters("access_token")
                        .addQueryParameter("access_token", newToken);

                Request newRequest = request.newBuilder()
                        .method(request.method(), request.body())
                        .url(newUrlBuilder.build())
                        .build();
                return chain.proceed(newRequest);
            }
        }
        return originalResponse;
    }


    /**
     * Access the interface, get data, and use it to determine token
     * @param originalResponse
     * @return
     * @throws IOException
     */
    private StatusVo getResult(final Response originalResponse) throws IOException {
        ResponseBody responseBody = originalResponse.body();
        BufferedSource source = responseBody.source();
        source.request(Long.MAX_VALUE);
        Buffer buffer = source.buffer();
        Charset charset = UTF8;
        MediaType contentType = responseBody.contentType();
        if (contentType != null) {
            charset = contentType.charset(UTF8);
        }
        String bodyString = buffer.clone().readString(charset);
        Gson gson = new Gson();

        //StatusVo is a public class that contains code, msg that all interfaces must return if you are in the background
        //Without a unified approach, you can also extract data directly from the body String to determine token expiration.
        StatusVo func = gson.fromJson(bodyString, StatusVo.class);
        return func;
    }

    //Refresh token
    private String getNewToken() throws IOException {
        //Get refresh token
        final String refreToken = (String) SPUtils.get(AppContext.getInstance(), SPUtils.REFRESH_TOKEN, "");;

        //Request to refresh the token interface to get a new token
        Call<RefreshTokenBean> objectObservable = Network.createService(NetWorkService.RefreshToken.class)
                .refresh(refreToken);
        RefreshTokenBean refreshTokenBean = objectObservable.execute().body();

        //Save the new token
        final String newToken = refreshTokenBean.getResult().getData().getAccess_token();
        final String newRefreshToken = refreshTokenBean.getResult().getData().getRefresh_token();
        SPUtils.put(AppContext.getInstance(), SPUtils.TOKEN, newToken);
        SPUtils.put(AppContext.getInstance(), SPUtils.REFRESH_TOKEN, newRefreshToken);
        return newToken;
    }
}
  • Step 1: Get the local Token and put it into the request url in the form of parameters, where token is added uniformly to avoid each interface passing token.
  • Step 2: Preload the returned data and intercept Token expiration
  • Step 3: Refresh Token and save it
  • Step 4: Use the new token to redirect Url and request it again

The above steps have written the interceptor, and then put the interceptor into OkHttp.

private OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(new TokenInterceptor())
            .build();

OK, that's OkHttp token.

Topics: OkHttp Android network