前言
今年是9102年了,应该没有还在用userId
来鉴权了吧,也应该很少人使用cookie
来保持会话了吧?而现在更常用的是Authorization
,
关于Authorization
简略的讲一讲Authorization,如果要深入了解的话请看底部的参考文章链接。Authorization的认证方式在我接触中有两种
Basic
Bearer
Basic
HTTP基本认证,在请求的时候加上以下请求头:
Authorization : basic base64encode(username+":"+password))
将用户名和密码用英文冒号(:)拼接起来,并进行一次Base64编码。服务端拿到basic码,然后自己查询相关信息再按照base64encode(username+":"+password))
的方式得出当前用户的basic进行对比。
Bearer
授权完成后会返回类似下面的数据结构:
{ "token_type": "Bearer", "access_token": "xxxxx", "refresh_token": "xxxxx"}
而其中的refresh_token
的作用是在access_token
失效的时候进行重新刷新传入的参数,具体怎么传要看各自项目的实现方式。access_token
就是我们的认证令牌。token_type是令牌的类型,而我现在使用到的只有bearer
,其它类型未碰到,希望各位看官能补充一下。
在使用的时候需要加上以下请求头:
Authorization : token_type access_token
也就是这样:
Authorization: Bearer xxxxx
实现
方式1 :authenticator
authenticator
是在创建OkHttpClient
的时候能够设置的一个方法,接收的是一个okhttp3.Authenticator
的interface,默认不设置的话是一个NONE
的空实现,而回调的地方是在okhttp3.internal.http.RetryAndFollowUpInterceptor.followUpRequest()
Authenticator
followUpRequest
编码
相关代码也比较简单,在okhttp3.Authenticator
的注释上面也写有简单的例子,核心代码就以下几行:
private Authenticator authorization = new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { //-----------核心代码------- // 这里抛出的错误会直接回调 onError // 这里发起的请求是同步的,刷新完成token后再增加到header中 // String token = refreshToken(); String token = Credentials.basic("userName", "password", Charset.forName("UTF-8")); return response.request() .newBuilder() .header("Authorization", token) .build(); //-----------核心代码------- } };
以上就是主要代码,其中演示的是basic
方式的认证模式,bearer
方式的没实现,其实也只是refreshToken()
中发起一个同步请求
去刷新一下token
并保存,后面的步骤都是一样的。
如何使用
创建OkHttpClient调用,当然,也可以直接写匿名内部类的实现,都是可以的。
retrofit = new Retrofit.Builder() .client(new OkHttpClient.Builder() .authenticator(authorization)// 增加重试 .addInterceptor(getHttpLoggingInterceptor()) .build()) .baseUrl("https://api.github.com/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
演示
为了演示方便看到结果,我在Authenticator
的实现类中增加了一些回调主线程的方法,具体看一下源码即可,对于主要结果没什么影响。
Authenticator
总结
使用官方提供的Authenticator
有一个很明显的问题,那就是会占用重试
,像示例中,我并没有传入一个正确的token
,就导致一直在回调Authenticator
,直到达到了最大重试次数为止。而往往需求是token失效以后选择重试一次,成功了继续请求,再次失败则提示登录,所以这个方法使用得不多。
方式2 :Interceptor
上面okhttp3.Authenticator
的实现方式其实是在RetryAndFollowUpInterceptor
中判断和回调的,由此,可以自定义一个Interceptor
,由开发者来自行判断和跳转。
编码
详细代码如下:
Interceptor mAuthenticatorInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { // 获取请求 Request request = chain.request(); // 获取响应 Response response = chain.proceed(request); // 在这里判断是不是是token失效 // 当然,判断条件不会这么简单,会有更多的判断条件的 if (response.code() == 401) { // 这里应该调用自己的刷新token的接口 // 这里发起的请求是同步的,刷新完成token后再增加到header中 // 这里抛出的错误会直接回调 onError// String token = refreshToken(); String token = Credentials.basic("userName", "password", Charset.forName("UTF-8")); // 创建新的请求,并增加header Request retryRequest = chain.request() .newBuilder() .header("Authorization", token) .build(); // 再次发起请求 return chain.proceed(retryRequest); } return response; } }
使用
和方法一相同,在创建HttpClient
的时候addInterceptor(mAuthenticatorInterceptor)
,将我们自己的拦截器加入进行即可。
演示
AuthenticatorInterceptor
总结
从演示中可以看出,在第一次返回401
的时候,进行了一次token的获取,并且再次进行了请求,圆满符合我们的预期,只重试一次。
最后
分析
可能会有疑问:为什么使用
Interceptor
就能达到我们预期的效果?Interceptor
到底是如何工作的?
首先Interceptor
添加是有先后顺序的,首先添加的是我们设置的Interceptor
,然后添加的才是okhttp
的Interceptor
。如源码中:
Add Interceptor
总的来说,okhttp
的实现方式就是通过Interceptor
来组成一个一个的chian
来实现的。每个Interceptor
里面的intercept()
方法内部都会调用Chain.proceed()
方法,将请求交给下一个Interceptor
,由此类推,一直到最后一个Interceptor
请求完成。
需要注意的是proceed
是同步的,也就是调用proceed
方法之后需要等等下一个Interceptor
进行处理,当最后一个Interceptor
请求到数据,经过自己的处理之后,再往上返回Response
,直到第一个Interceptor
为止,返回数据。主要关系如下图:
Interceptor ex
这些所有的Interceptor
里面的proceed
都是调用了一次,那么我们增加一个Interceptor
,等到proceed
返回了Response
之后,对Response
进行判断,如果是认证失败,我们则刷新一下token,重新创建Request,再调用一次proceed
方法。如果再失败了,就不会再回调到当前的Interceptor
,如下图:
AuthenticatorInterceptor
作者:FullScreenDev
链接:https://www.jianshu.com/p/7a2d2d7497a1
共同學習,寫下你的評論
評論加載中...
作者其他優質文章