将 Cookies 添加到 Retrofit 2 请求。

35

我需要在Retrofit 2.0中添加Cookies。如果我理解正确,Cookies和Headers相同。这些Cookies必须被添加:

private HashMap<String, String> cookies = new HashMap();
cookies.put("sessionid", "sessionId");
cookies.put("token", "token");
这可以使用Jsoup库来完成。
String json = Jsoup.connect(apiURL + "/link")
                    .cookies(cookies)
                    .ignoreHttpErrors(true)
                    .ignoreContentType(true)
                    .execute()
                    .body();

这是我使用Retrofit请求的代码:

@GET("link")
Call<CbGet> getData(@Header("sessionid") String sessionId, @Header("token") String token);

但它不起作用了...我得到了403错误代码,因此请求中没有cookie...

有什么建议吗?


https://dev59.com/0FsW5IYBdhLWcg3wg3iD - USKMobility
7个回答

54

首先:Cookie 不同于头部信息。Cookie 是一个特殊的 HTTP 头部,名为 Cookie,后面跟着一组键值对(用“;”分隔)。例如:

Cookie: sessionid=sessionid; token=token

由于您不能在同一请求中设置多个Cookie头部,因此您无法使用两个@Header注释来分别设置值(在您的示例中为sessionidtoken)。我可以想到一个非常繁琐的解决方法:

@GET("link")
Call<CbGet> getData(@Header("Cookie") String sessionIdAndToken);

您更改了发送sessionId和token的方法,现在需要手动预先生成cookie字符串。在您的情况下,sessionIdAndToken字符串对象应该等于"sessionid=这里放sessionid; token=这里放token"。适当的解决方案是通过添加OkHttp的(即Retrofit用于进行基础http调用的库)请求拦截器来设置cookie。您可以在这里看到添加自定义标题的解决方案。

20

为什么不使用cookieJar选项?

new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(
        new OkHttpClient().newBuilder()
            .cookieJar(new SessionCookieJar()).build())
    .build();

private static class SessionCookieJar implements CookieJar {

    private List<Cookie> cookies;

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        if (url.encodedPath().endsWith("login")) {
            this.cookies = new ArrayList<>(cookies);
        }
    }


    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        if (!url.encodedPath().endsWith("login") && cookies != null) {
            return cookies;
        }
        return Collections.emptyList();
    }
}

也许你忘记在saveFromResponse方法中添加this.cookies.addAll(cookies);了。 - blueware
不,您不想添加到 cookies 中。您需要在 saveFromResponse 中完全重置 cookies。 - Mohit Atray

5

API Web服务示例添加头

@FormUrlEncoded
@POST("getDriverRoutes")
Call<Guzergah> getGuzergah(@Header("Cookie") String userCookie, @Field("driver_id") int driver_id);

当您登录系统时,通过Response将cookie保存到本地。

 public void onResponse(Response<Login> response, Retrofit retrofit) {
            hidepDialog();
            String cookie = response.headers().get("Set-Cookie");
            editor.putString("user_cookie", cookie.split(";")[0]);
            editor.commit();

在每个需要授权发送Cookie的服务中,您需要从SharePreference获取Cookie并将其与Web服务一起发送。

  String userCookie = shp.getString("user_cookie", ""); // here is cookies before send
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://server.aracadabra.com:8080/").
                    addConverterFactory(GsonConverterFactory.create())
            .build();

    final APIService service = retrofit.create(APIService.class);

    final Call<SenMsgStatus> loginMCall = service.sendMessage(userCookie, type, route_id, user_id, passenger_id, passenger_status, text);

4

要在OkHttp请求头中添加cookie头,请在拦截器中添加以下内容:

request= request.newBuilder().addHeader("Cookie", sessionIdAndToken).build();

参考:https://github.com/square/okhttp/wiki/Interceptors ,由 @Alexander Mironov 提供。

完整的示例代码请参见此处:希望能对您有所帮助。

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;


public class LoggingInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();

        long t1 = System.nanoTime();
        request = processRequest(request);
        logger.info("Sending request with url:{} and connection status:{} and request headers:{}",
                request.url(), chain.connection(), request.headers());

        Response response = chain.proceed(request);
        long t2 = System.nanoTime();
        logger.info("Received response for url:{} and time taken:{} and response headers as:{}",
                response.request().url(), (t2 - t1) / 1e6d, response.headers());

        response = processResponse(response);
        return response;
    }
    //Add cookies for all subsequent requests
    private Request processRequest(Request request) {
        if(!request.url().toString().contains("/v1/authorize")) {
            final EmployeeSession session = getSession();
            StringBuilder etokenAndUId = new StringBuilder("etoken=");
            etokenAndUId.append(session.getEtoken()).append(";").append("uId=").append(session.getUId());
            logger.info("inside processRequest for all subsequent requests, sending cookies as etokenAndUId values:{}", etokenAndUId.toString());
            request= request.newBuilder().addHeader("Cookie", etokenAndUId.toString()).build();
        }
        return request;
    }

    //Extract cookies from login url
    private Response processResponse(Response response) {
        if(response.request().url().toString().contains("/v1/authorize")) {
            final List<String> cookieHeaders = response.headers("Set-Cookie");
            String etoken = "", uId = "";
            for (String cookieHeader : cookieHeaders) {
                if(cookieHeader.contains("etoken=")) {
                    etoken = cookieHeader.substring(7, cookieHeader.indexOf(';'));
                }
                else if(cookieHeader.contains("uId=")) {
                    uId = cookieHeader.substring(4, cookieHeader.indexOf(';'));
                }
            }
            logger.info("inside processResponse found cookies from login url response: etoken:{} and uid:{}", etoken, uId);
            setSession(etoken, uId, "c1");
            logger.info("current employee session:{}", getSession());
        }
        return response;
    }
 }    

2
您可以通过拦截器在Retrofit中添加Cookie,这是我们在API请求中动态管理Cookie的最佳解决方案。
我尝试过使用JavaNetCookieJar(cookieManager),但在我的情况下无法工作。因此,我使用了Interceptor,在依赖注入(Dagger和Koin)中也可以使用。
步骤1:创建ReceivedCookiesInterceptor类来接收Cookie。
Java代码:ReceivedCookiesInterceptor.java
public class ReceivedCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
    Response originalResponse = chain.proceed(chain.request());

    if (!originalResponse.headers("Set-Cookie").isEmpty()) {
        HashSet<String> cookies = new HashSet<>();

        for (String header : originalResponse.headers("Set-Cookie")) {
          cookies.add(header);
        }

        Preferences.getDefaultPreferences().edit()
                .putStringSet(Preferences.PREF_COOKIES, cookies)
                .apply();
    }

    return originalResponse;
} 
}

Kotlin代码:ReceivedCookiesInterceptor.Kt

class ReceivedCookiesInterceptor : Interceptor {
        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response {
            val originalResponse: Response = chain.proceed(chain.request())
            if (originalResponse.headers("Set-Cookie").isNotEmpty()) {
                val cookies: HashSet<String> = HashSet()
                for (header in originalResponse.headers("Set-Cookie")) {
                    cookies.add(header)
                }
                preferenceProvider.putStringSet("PREF_COOKIES", cookies)  
                // Prefrence Provider In My SharedPrefrence Object
            }
            return originalResponse
        }
    }

步骤2:创建AddCookiesInterceptor类以存储cookies。
Java代码:AddCookiesInterceptorclass.java
public class AddCookiesInterceptor implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
    Request.Builder builder = chain.request().newBuilder();
    HashSet<String> preferences = (HashSet) Preferences.getDefaultPreferences().getStringSet(Preferences.PREF_COOKIES, new HashSet<>());
    for (String cookie : preferences) {
        builder.addHeader("Cookie", cookie);
        Log.v("OkHttp", "Adding Header: " + cookie); // This is done so I know which headers are being added; this interceptor is used after the normal logging of OkHttp
    }

    return chain.proceed(builder.build());
}
}

Kotlin代码:AddCookiesInterceptorclass.kt

        class AddCookiesInterceptor : Interceptor {
        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response {
            val builder: Request.Builder = chain.request().newBuilder()
            val prefCookies: HashSet<String> =
                preferenceProvider.getStringSet("PREF_COOKIES", HashSet())
            for (cookie in prefCookies) {
                builder.addHeader("Cookie", cookie)
            }
            return chain.proceed(builder.build())
        }
    }

步骤3:将这些拦截器添加到您的okHttpClientObject中

Java代码:在Retrofit请求中添加拦截器

OkHttpClient okHttpClient = new OkHttpClient();
     okHttpClient.interceptors().add(new AddCookiesInterceptor());
    okHttpClient.interceptors().add(new ReceivedCookiesInterceptor());

Kotlin代码:为Retrofit请求添加拦截器

   val okHttpClient = OkHttpClient().newBuilder()
        .connectionSpecs(specs)
        .addInterceptor(requestInterceptor)
        .addInterceptor(AddCookiesInterceptor())
        .addInterceptor(ReceivedCookiesInterceptor())

很好的实现了保存Cookie并在Retrofit中应用。 - undefined

0

这里是一些 Kotlin 代码,它调用了在传递给 Retrofit2 的 OkHttpClient (v3) 中的 CookieManager

    val client = OkHttpClient.Builder()
        .cookieJar(object : CookieJar {
            private val cookieStore = HashMap<HttpUrl?, List<Cookie>>()
            private val cookieManager = CookieManager.getInstance()

            override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
                val host = url.host()
                cookieStore[HttpUrl.parse(host)] = cookies
                cookieManager.removeAllCookie()
                val cookies1 = cookieStore[HttpUrl.parse(host)]
                if (cookies1 != null) {
                    for (cookie in cookies1) {
                        val cookieString =
                            cookie.name() + "=" + cookie.value() + "; path=" + cookie.path()
                        cookieManager.setCookie(cookie.domain(), cookieString)
                    }
                }
            }

            override fun loadForRequest(url: HttpUrl): List<Cookie> {
                val host = url.host()
                val cookies = cookieStore[HttpUrl.parse(host)]
                return cookies ?: ArrayList()
            }
        })
        .build()


    CookieManager.getInstance().setAcceptCookie(true)

    val retrofit2 = Retrofit.Builder().baseUrl(baseUrl)
            // converter factory for converting JSON to object
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(client)
            .build()

在你的 build.gradle 文件中的 dependencies 部分:

dependencies {
    ...
    implementation 'androidx.webkit:webkit:1.5.0'

0

使用拦截器

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.http.*

interface FileService {
    companion object {
        private var fileService: FileService? = null
        fun getInstance(cookie: String): FileService {
            if (fileService == null) {
                fileService = Retrofit
                    .Builder()
                    .baseUrl("xxxxxx")
//                    .addConverterFactory(GsonConverterFactory.create())
                    //add cookie
                    .client(OkHttpClient().newBuilder().addInterceptor(Interceptor { chain ->
                        chain.proceed(
                            chain.request().newBuilder().addHeader("Cookie", cookie).build()
                        );
                    }).build())
                    .build()
                    .create(FileService::class.java)
            }
            return fileService!!
        }
    }
@GET("link")
Call<CbGet> getData();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接