How to get more than one Set-Cookie header from response using Retrofit/OkHttpClient?

。_饼干妹妹 提交于 2021-02-08 04:41:43

问题


I'm trying to make an authentication call using Retrofit/OkHttpClient on Android. The server responds a 302 with two Set-Cookie headers and okHttp handles redirection. I found some tips to get those cookies using cookieManager. But it seems cookieManager reads only one Set-Cookie header.

I have this in server response headers:

Set-Cookie:first=1
Set-Cookie:second=2

This is my cookieManager part:

private static CookieManager cookieManager;
(...)
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setFollowSslRedirects(true);
okHttpClient.setAuthenticator(new NTLMAuthenticator(user, passwd, domain));

cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
okHttpClient.setCookieHandler(cookieManager);

And handling Set-Cookie headers:

public void checkForSetCookies() {
    List<retrofit.client.Header> headerList = new ArrayList<>();
    List <HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
    //List cookies has only one cookie:  first=1
    for (HttpCookie cookie : cookies) {
        retrofit.client.Header header = new retrofit.client.Header("Set-Cookie", cookie.toString());
        headerList.add(header);
    }

    if (headerList.size() > 0) {
        Commons.setLoginRetofitCookies(headerList);
    }
}

Is there a way to force cookieManager to read both of my Set-Cookie headers?

Thank You in advance for help.


回答1:


To get multiple "Set-Cookie" header from response :

 Call<LoginResponse> call = apiService.login(loginRequest);
    call.enqueue(new Callback<LoginResponse>() {
        @Override
        public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
            showProgress(false);
            if (response.body() != null) {

                LoginResponse loginResponse = response.body();
                //Get headers from response
                Headers headerResponse = response.headers();
                //convert header to Map
                Map<String, List<String>> headerMapList = headerResponse.toMultimap();
                //Get List of "Set-Cookie" from Map 
                List<String> allCookies = headerMapList.get("Set-Cookie");
                String cookieval = "";
                for (int i = 1; i < allCookies.size(); i++) {
                    allCookies.get(i);
                    //concat all cookies in cookieval.
                    cookieval = cookieval + allCookies.get(i);
                }
                //Save cookies value in Application class.
                ((AppConfig) getApplication()).setCookies(cookieval);

                ((AppConfig) getApplication()).setUserInfo(loginResponse);
                if (loginResponse.getStatus().equals("ok")) {
                    startActivity(new Intent(LoginActivity.this, MainActivity.class));
                    mAuthTask = true;
                    finish();
                } else {
                    mPasswordView.setError(getString(R.string.error_incorrect_password));
                    mPasswordView.requestFocus();
                }
            }
        }

        @Override
        public void onFailure(Call<LoginResponse> call, Throwable t) {
            Log.d(TAG, "onFailure: ");
            mAuthTask = false;
        }
    });

To pass all the cookies in other service (For Eg. Logout Service)

 @Headers({"Content-Type: application/json"})
@POST("request/get/user/logout")
Call<LogoutResponse> logout(@Header("Cookie") String cookie);

Add this code in Api interface

Now call service of logout like:

Call<LogoutResponse> call = apiService.logout(((AppConfig) getApplication()).getCookies());
    call.enqueue(new Callback<LogoutResponse>() {
        @Override
        public void onResponse(Call<LogoutResponse> call, Response<LogoutResponse> response) {
            if(response.body() != null){
                LogoutResponse logoutResponse = response.body();
                if (logoutResponse.getStatus().equals("ok")) {
                    finish();
                    startActivity(new Intent(MainActivity.this, LoginActivity.class));
                }
            }
        }

        @Override
        public void onFailure(Call<LogoutResponse> call, Throwable t) {
            Log.d(TAG, "onFailure: ");
        }
    });



回答2:


You can write an extension function with Kotlin.

fun Headers.getCookies(): String? {
    var cookieString: String? = null
    this.toMultimap()["Set-Cookie"]?.forEach {
    cookieString += it
    }
    return cookieString
}



回答3:


I think I solved the problem. CookieManager class is parsing response headers to list:

List<HttpCookie> cookies = parseCookie(responseHeaders);

so as a result the second cookie is overwritten.

I decided to create MyCookieManager inner class and override its put method to put cookies values to list of Strings. I call super method to do all headers validation.

class MyCookieManager extends CookieManager {

    @Override
    public void put(URI uri, Map<String, List<String>> stringListMap) throws IOException {
        super.put(uri, stringListMap);
        if (stringListMap != null && stringListMap.get("Set-Cookie") != null)
            for (String cookieValue: stringListMap.get("Set-Cookie")) {
                cookiesStrings.add(cookieValue);
            }
    }
}

Now it works fine :)



来源:https://stackoverflow.com/questions/34181175/how-to-get-more-than-one-set-cookie-header-from-response-using-retrofit-okhttpcl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!