1.本来打算用已经对RxJava提供支持的Retrofit2实现,但Retrofit2的文档基本没有,没有找到可以获取Cookie、对status code处理的方法,所以还是用okHttp进行了实现
2.上周开始学习RxJava,是想解决多个网络请求嵌套访问的问题,CAS算是比较典型的多个同步请求后才能获取实际需要的内容。
3.代码中用到了”如何用okHttp进行Cookie的自动管理”,App这边对应Cas Server,服务A,服务B,这样至少要处理3个域名下的Cookie,自动管理是不能少的。代码来自http://stackoverflow.com/questions/25461792/persistent-cookie-store-using-okhttp-2-on-android
4.这次App实现决定采用了MVP模式,一个人做的好处就是可以很快的实现的自己的想法,当时觉得太麻烦了,那么多接口,重用可能性很小……这次登录请求由原来的基于Volley的普通请求改成现在的代码,幸亏用了MVP:)
依赖:
compile 'com.squareup.okhttp:okhttp:2.6.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.6.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.google.code.gson:gson:2.5'
compile 'com.jakewharton.timber:timber:4.1.0'
CAS的Rest接口:
http://jasig.github.io/cas/4.1.x/protocol/REST-Protocol.html
- 以用户名和密码访问CAS Server获取 TGT
- 用1返回的TGT和约定好的服务端Service地址访问CAS Server,获取ST
- 用ST请求Service地址,获取Cookie
1.封装获取TGT请求返回Observable
public static Observable<String> getTGT(final String account, final String password) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {
try {
String tgtPath = casServer + "/v1/tickets";
FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
formEncodingBuilder.add("username", account);
formEncodingBuilder.add("password", password);
RequestBody formBody = formEncodingBuilder.build();
Request request = new Request.Builder()
.url(tgtPath)
.post(formBody)
.build();
Response response = mOkHttpClient.newCall(request).execute();
int code = response.code();
if (201 == code) {
String location = response.header("Location");
String tgt = location.substring(location.lastIndexOf("/") + 1);
subscriber.onNext(tgt);
} else {
System.out.println(code);
System.out.println(response.body().string());
subscriber.onError(new Exception("error"));
}
} catch (Exception e) {
subscriber.onError(e);
}
}
});
}
2.获取ST
public static Observable<String> getST(final String tgt, final String service) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {
try {
String stPath = casServer + "/v1/tickets/" + tgt; // POST /cas/v1/tickets/{TGT id} HTTP/1.0
FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
formEncodingBuilder.add("service", service);
RequestBody formBody = formEncodingBuilder.build();
Request request = new Request.Builder()
.url(stPath)
.post(formBody)
.build();
Response response = mOkHttpClient.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
String st = response.body().string();
subscriber.onNext(st);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
});
}
3.换取Cookie
public static Observable< List<String>> getCookie(final String st, final String service){
return Observable.create(new Observable.OnSubscribe< List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
try {
String cookiePath = service + "?" + "ticket=" + st;
Request request = new Request.Builder()
.url(cookiePath)
.build();
Response response = mOkHttpClient.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
List<String> list = response.headers("Set-Cookie");
subscriber.onNext(list);
subscriber.onCompleted();
}catch (Exception e){
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
整合
public void login(final String account, final String password, final String service) {
getTGT(account, password)
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String tgt) {
return getST(tgt, service);
}
})
.flatMap(new Func1<String, Observable<List<String>>>() {
@Override
public Observable<List<String>> call(String st) {
return getCookie(st, service);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {
@Override
public void onCompleted() {
Timber.e("onCompleted");
}
@Override
public void onError(Throwable e) {
Toast.makeText(LoginActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
Timber.e("onError");
e.printStackTrace();
}
@Override
public void onNext(List<String> s) {
Toast.makeText(LoginActivity.this, s.toArray().toString(), Toast.LENGTH_SHORT).show();
Timber.e("onNext");
}
});
}