处理验证
这部分和HTTP AUTH有关.
HTTP AUTH
使用HTTP AUTH需要在server端配置http auth信息, 其过程如下:
客户端发送http请求
服务器发现配置了http auth, 于是检查request里面有没有”Authorization”的http header
如果有, 则判断Authorization里面的内容是否在用户列表里面, Authorization header的典型数据为”Authorization: Basic jdhaHY0=”, 其中Basic表示基础认证, jdhaHY0=是base64编码的”user:passwd”字符串. 如果没有,或者用户密码不对,则返回http code 401页面给客户端.
标准的http浏览器在收到401页面之后, 应该弹出一个对话框让用户输入帐号密码; 并在用户点确认的时候再次发出请求, 这次请求里面将带上Authorization header.
一次典型的访问场景是:
浏览器发送http请求(没有Authorization header) 服务器端返回401页面 浏览器弹出认证对话框 用户输入帐号密码,并点确认 浏览器再次发出http请求(带着Authorization header) 服务器端认证通过,并返回页面 浏览器显示页面
OkHttp认证
OkHttp会自动重试未验证的请求. 当响应是401 Not Authorized时,Authenticator会被要求提供证书. Authenticator的实现中需要建立一个新的包含证书的请求. 如果没有证书可用, 返回null来跳过尝试.
使用Response.challenges()来获得任何authentication challenges的 schemes 和 realms. 当完成一个Basic challenge, 使用Credentials.basic(username, password)来解码请求头.
Basic Authentication认证
private final OkHttpClient client;
public Demo() {
client = new OkHttpClient.Builder()
.authenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
})
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}喜欢使用lambda表达式如下
client = new OkHttpClient.Builder()
.authenticator((route, response) -> {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
})
.build();正确授权,打印结果如下
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
authorization:null
@@@@@\
@@@@@@@@
@@@@@@@@@@@@@@@@@@@.
@/ \@@@@@@@@@@@@@@@@.
@ @@@@@@@@@@@@@@@@+
@\ /@@@@@@@@"*@@/^@/
\@@@@@@@@@@/ " "
@@@@@@@@
@@@@@/
:@@@.
.@@@@@@@: +@@ `@@ @@` @@ @@
.@@@@'@@@@: +@@ `@@ @@` @@ @@
@@@ @@@ +@@ `@@ @@` @@ @@
.@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@
@@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@
@@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@
@@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+
@@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@#
@@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+
@@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@
@@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@
@@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@
@@:
@@:
@@:
耗时:2383如果输入错误的授权账号,打印结果如下
.... 省略部分 ....
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
authorization:Basic eHg6eHg=
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
authorization:Basic eHg6eHg=
java.net.ProtocolException: Too many follow-up requests: 21
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:171)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
at com.tingfeng.guide.Test.run(Test.java:40)
at com.tingfeng.guide.Test.main(Test.java:49)为避免当验证不工作而导致许多重试,你可以返回null放弃,例如:当这些确切的证书已经尝试访问过时你可能想跳过重试:
client = new OkHttpClient.Builder()
.authenticator((route, response) -> {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("xx", "xx");
if (credential.equals(response.request().header("Authorization"))) {
System.out.println("验证失败,返回null");
return null;
}
return response.request().newBuilder()
.header("Authorization", credential)
.build();
})
.build();打印结果如下,这种方式会在第三次认证失败返回null
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
authorization:null
Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
Challenges: [Basic realm="OkHttp Secrets" charset="ISO-8859-1"]
authorization:Basic eHg6eHg=
认证失败,返回null
java.io.IOException: Unexpected code Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}
at com.tingfeng.guide.Test.run(Test.java:41)
at com.tingfeng.guide.Test.main(Test.java:49)当你设置一个应用程序定义的限制时你也可以跳过重试
client = new OkHttpClient.Builder()
.authenticator((route, response) -> {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("xx", "xx");
String authorization = response.request().header("Authorization");
System.out.println("authorization:"+ authorization);
// 第5次认证失败返回null
if (responseCount(response) >= 5) {
System.out.println("认证失败,返回null");
return null; // If we've failed 5 times, give up.
}
return response.request().newBuilder()
.header("Authorization", credential)
.build();
})
.build();这上面的代码依赖于 responseCount() 方法:
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}Bearer Token认证
使用Header传递Authorization认证参数
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.addHeader("Authorization","Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0aW5nZmVuZyIsInJvbGVzIjpbIk1FTUJFUiJdLCJpYXQiOjE1MjM0Mzg4ODYsImV4cCI6MTUyMzQ0MjQzMH0.5oQU1HUekYxP6BE534Vek_O6ZXhwPbUXQJuBB_da8r8")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}测试
public static void main(String[] args) throws IOException {
PostExample example = new PostExample();
String json = "{\"username\":\"tingfeng\",\"password\":\"tingfeng\"}";
String response = example.post("http://localhost:8080/secure/user/roles", json);
System.out.println(response);
}未经允许请勿转载:程序喵 » OkHttp3 之 Authorization处理认证(四)
程序喵