最近公司在改造接口的请求的验证,之前是登陆后返回一个 token ,在请求的时候动态添加到 header 中,以此来验证身份,当返回 401 直接去重新登录;现在登录返回 token 和 refreshToken 两个参数,拿 token 去添加 header ,当返回 401 时并不直接去登录而是拿 refreshToken 去请求一个接口,刷新得到新的 token 和 refreshToken ,拿到新的 token 再去请求当前返回 401 的接口,如果此时返回 410 则是真正的过期才需要去登录。
Response response = chain.proceed(builder.build()); ResponseBody responseBody = response.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); CustomResponse customResponse = new Gson().fromJson(bodyString, CustomResponse.class); String code = customResponse.getCode();//后台的返回码 String msg = customResponse.getMsg(); if ("401".equals(code)) { //todo 当返回401时去刷新token } //否则正常返回 response 复制代码
Map<String, String> map = new ArrayMap<>(); map.put("refreshToken", refreshToken);//这是我们在登录成功后返回的refreshToken,专门用于刷新操作的 RequestBody body = NetworkUtils.setBody(map); Call<CustomResponse<Map<String, String>>> call = RetrofitUtils.provideClientApi().refreshToken(body); CustomResponse refreshResponse = call.execute().body(); Map<String, String> mapToken = (Map<String, String>) refreshResponse.getData(); String refreshCode = refreshResponse.getCode(); 复制代码
刷新成功后有两种操作,如果返回 200 ,拿到新的 token 去重新请求当前报 401 的接口,如果返回 410(当然也可以是110,因为这是咋们和后台小伙伴约定的 这个时候就是 token 真正的过期了,直接去重新登录。
#####重新请求,我们此时只需要拿到上次请求的 request ,因为我们拦截了响应当前拦截器中的 request 就是我们之前报 401 的请求,但是 ,然后返回response,也可以在这个response中继续拦截操作
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request();//这里的request只是为了拿到请求的url和参数,下面要重新生成request(builder.build()) Request.Builder builder = request.newBuilder() .addHeader("Content-Type", "application/json; charset=UTF-8") .addHeader("Authorization", newToken); //注意:chain.proceed(这里一定不能是拿到的request,而是builder.build()) return chain.proceed(builder.build()); } 复制代码
好了,完成
synchronized (mContext) { Map<String, String> map = new ArrayMap<>(); map.put("refreshToken", refreshToken);//这是我们在登录成功后返回的refreshToken,专门用于刷新操作的 RequestBody body = NetworkUtils.setBody(map); Call<CustomResponse<Map<String, String>>> call = RetrofitUtils.provideClientApi().refreshToken(body); CustomResponse refreshResponse = call.execute().body(); Map<String, String> mapToken = (Map<String, String>) refreshResponse.getData(); String refreshCode = refreshResponse.getCode(); } 复制代码
其实仔细想想,如果我们已经刷新过 token 了,那就直接拿最新的 newToken 去重新请求当前接口就好了,我们拿到最新的 token 肯定是需要保持成全局的,而我们所有的请求是异步的,那就可以拿到每次的 request ,这意味着什么?我们就可以拿到 header ,那之前过期的 token 就有了;二者一对比,一样则说明还没有刷新过 token ,那就先去刷新 token ,不一样说明已经有接口刷新过了直接拿最新 newToken 的去重新请求就好了。(就是一个判断就不贴代码了【偷笑】)
String oldToken = request.header("Authorization"); String oldToken = request.headers().get("Authorization"); 复制代码
Request.Builder builder = request.newBuilder() .addHeader("Content-Type", "application/json; charset=UTF-8") .addHeader("Authorization", newToken); 复制代码
String oldJwt = response.request().headers().get("Authorization"); 复制代码
if ("401".equals(code)) { synchronized (mContext) { refreshToken = "获取最新的refreshToken" token = "获取最新的token" String oldJwt = response.request().headers().get("Authorization"); /** * 当前请求中的jwt和本地最新的是否一样: * 1、一样则说明没有进行刷新jwt操作不进入此 if * 2、不一样则说明已经刷新过jwt操作了,进入此 if 拿最新的jwt直接重新发起当前的请求 */ if (!jwt.equals(oldJwt)) { Request.Builder newBuilder = getBuilder(chain.request(), token); return getNewResponse(chain, newBuilder); } Map<String, String> mapToken = refreshMapJwt(refreshJwt); String newToken = mapJwt.get("token"); String newRefreshToken = mapToken.get("refreshToken"); MyApplication.setToken(newToken);//设置为全局常量 "此处还需要的一个操作是把二者都保存到本地,不然下次登录就没了" Request.Builder newBuilder = getBuilder(chain.request(), newJwt); return getNewResponse(chain, newBuilder); } } 复制代码