上周六开始,app主页上通过webview访问服务器H5页面的功能点开都跳转登录页,前同事做了webview cookie同步失效,这个问题把我折腾了三天,终于搞定!写个博客记录一下。
正文开始前,咱们先简单回顾一下cookie和session。用户输入用户名和密码,服务器验证用户信息成功后创建一个session,将session id放在cookie返回给客户端。
服务端代码:
HttpSession session = request.getSession();
String sid = session.getId();
response.addHeader("Set-Cookie","JSESSIONID="+sid+";Domain=.;Path=/");
客户端代码:
private void initButtonLogin() {
mButtonLogin = (Button) findViewById(R.id.tv_login);
mButtonLogin.setOnClickListener(v -> {
//防止短时间多次点击
if(BtnUtil.isValidClick()){
login();
login();//为什么要向服务器发送两遍登录请求?因为一定要发第二遍(cookie的两次握手),webview cookie才会同步成功,webview才能追踪到此session。有知道的童鞋可以告知一下,互相学习~
}
});
}
private void login(){
String userId = mEditUserId.getText().toString().trim();
String passWord = mEditPassWord.getText().toString().trim();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mEditUserId.getWindowToken(), 0); //强制隐藏键盘
imm.hideSoftInputFromWindow(mEditPassWord.getWindowToken(), 0); //强制隐藏键盘
OkHttpClient client= new OkHttpClient().newBuilder()
.connectTimeout(3000, TimeUnit.MILLISECONDS)
.cookieJar(new MyCookieJar())
.build();
//get请求
String path= ApiConstant.PROJECT_URL+"api/login?userid="+userId+"&password="+passWord;
Request request=new Request.Builder().url(path).get().build();
try {
Response response = client.newCall(request).execute();
String result = response.body().string();
JSONObject jsonObject = JSON.parseObject(result);
JSONArray arr = jsonObject.getJSONArray("data");
//此处代码无关主题,隐藏
} catch (Exception e) {
e.printStackTrace();
ToastUtils.showShort("登录失败,请与管理员联系!");
}
}
MyCookieJar.java
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
/**
* cookie管理
*/
public class MyCookieJar implements CookieJar {
@Override
public void saveFromResponse(@NonNull HttpUrl url, @NonNull List<Cookie> cookies) {
try {
String cookieStr = encodeCookie(cookies);
saveCookie(url.host(), cookieStr);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public List<Cookie> loadForRequest(@NonNull HttpUrl url) {
List<Cookie> cookies = new ArrayList<>();
String cookieStr = loadCookie(url.host());
if (!TextUtils.isEmpty(cookieStr)) {
//获取所有 Cookie 字符串
String[] cookieStrs = cookieStr.split("#");
for (String aCookieStr : cookieStrs) {
//将字符串解析成 Cookie 对象
Cookie cookie = Cookie.parse(url, aCookieStr);
cookies.add(cookie);
}
}
//此方法返回 null 会引发异常
return cookies;
}
private String encodeCookie(List<Cookie> cookies) {
StringBuilder sb = new StringBuilder();
for (Cookie cookie : cookies) {
//将Cookie转换成字符串
sb.append(cookie.toString());
//以#为分隔符
sb.append("#");
}
if(sb.length() > 0){
sb.deleteCharAt(sb.lastIndexOf("#"));
}
return sb.toString();
}
public static String loadCookie(String host) {
SharedPreferences sp = BaseApplication.getAppContext()
.getSharedPreferences("cookie_oref", Context.MODE_PRIVATE);
if (!TextUtils.isEmpty(host) && sp.contains(host)) {
return sp.getString(host, "");
}
return null;
}
private void saveCookie(String host, String cookie) {
SharedPreferences.Editor editor = BaseApplication.getAppContext()
.getSharedPreferences("cookie_oref", Context.MODE_PRIVATE)
.edit();
if (!TextUtils.isEmpty(host)) {
editor.putString(host, cookie);
}
editor.apply();
}
}
webview cookie同步:
private void syncCookie(String url) {
HttpUrl okurl = HttpUrl.parse(ApiConstant.PROJECT_URL + url);
String cookies = MyCookieJar.loadCookie(okurl.host());
if(!TextUtils.isEmpty(cookies)){
try {
createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
String[] cookieArray = cookies.split("#");
for (int i = 0; i < cookieArray.length; i++) {
cookieManager.setCookie(ApiConstant.PROJECT_URL+url, cookieArray[i]);//为url设置cookie
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
cookieManager.flush();
}else{
CookieSyncManager.getInstance().sync();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结这次解决问题的时间都浪费在没有把cookie和session关联起来,其实把session id放在cookie传到客户端即可。到目前还有一点不明白的就是,为什么一定要发第二遍登录请求,webview cookie才会同步过去,有知道的童鞋请不吝赐教!hiahiahia~