使用 OkHttp 3 自动处理 cookie

Posted

技术标签:

【中文标题】使用 OkHttp 3 自动处理 cookie【英文标题】:Automatic cookie handling with OkHttp 3 【发布时间】:2016-04-25 05:17:40 【问题描述】:

我正在使用 okhttp 3.0.1。

我得到的每个地方都使用 okhttp2 处理 cookie

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

请有人指导我如何在版本 3 中使用。setCookieHandler 方法在版本 3 中不存在。

【问题讨论】:

一些遗留的东西落在了我的腿上,我寻找如何使用 OkHttp 2 做到这一点。你的问题就是我的答案。 :-) 【参考方案1】:

如果您想使用新的 OkHttp 3 CookieJar 并摆脱 okhttp-urlconnection 依赖项,您可以使用此 PersistentCookieJar。

您只需要创建一个PersistentCookieJar 的实例,然后将其传递给OkHttp 构建器即可:

CookieJar cookieJar =
                    new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .cookieJar(cookieJar)
                    .build();

【讨论】:

非常感谢您的方法。非常适合我的目的。 很棒的伙伴为此浪费了 2 天的时间......终于让它与你的方法一起工作。对于那些使用retrofit2 的人,请使用jar 而不是persistancecookie 存储类。 @franmontiel 库工作正常,但是当我们关闭应用程序并返回应用程序时,它不会保留 cookie,因此 api 在第一次调用 web api 时就获得了新的 cookie。我有你的库的单个实例并从应用程序类调用。你能告诉我如何纠正它吗? @AshuKumar 可能是由于您的 cookie 没有过期日期。这将使它们成为会话 cookie,因此它们不应被持久化。 感谢您回复我。真的非常感谢……@franmontiel。我的应用要求是不要让 cookie 过期,如果 cookie 过期,那么会话将不一样,因此客户将自动注销。你能告诉我任何解决方案或解决方法吗,会好得多。【参考方案2】:

现在我正在玩它。 试试PersistentCookieStore,为JavaNetCookieJar添加gradle依赖:

compile "com.squareup.okhttp3:okhttp-urlconnection:3.0.0-RC1"

和初始化

    // init cookie manager
    CookieHandler cookieHandler = new CookieManager(
            new PersistentCookieStore(ctx), CookiePolicy.ACCEPT_ALL);
    // init okhttp 3 logger
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    // init OkHttpClient
    OkHttpClient httpClient = new OkHttpClient.Builder()
            .cookieJar(new JavaNetCookieJar(cookieHandler))
            .addInterceptor(logging)
            .build();

`

【讨论】:

仅供参考:在不久的将来,他们计划删除 okhttp-urlconnection 模块。 github.com/square/okhttp/blob/master/CHANGELOG.md【参考方案3】:

在这里,您有一个创建自己的 CookieJar 的简单方法。它可以随心所欲地扩展。我所做的是实现一个 CookieJar 并使用 OkHttpClient.Builder 和这个 CookieJar 构建 OkHttpClient。

public class MyCookieJar implements CookieJar 

    private List<Cookie> cookies;

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) 
        this.cookies =  cookies;
    

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) 
        if (cookies != null)
            return cookies;
        return new ArrayList<Cookie>();

     

以下是创建 OkHttpClient 的方法

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cookieJar(new MyCookieJar());
OkHttpClient client = builder.build();

【讨论】:

这真的很好用!我的猜测是,抱怨它不起作用的人没有意识到他们需要在整个应用程序中使用相同的 MyCookieJar 实例才能使 cookie 在其他地方工作。他们还需要了解 cookie 不会在应用程序启动时持续存在(一旦应用程序被杀死并释放内存),因为它们没有被存储。不过对我来说这很完美。 安全警告:此 CookieJar 实现将所有 cookie 发送到所有站点。永远不要在生产中使用它! 正如 vonox7 指出的那样,这个解决方案是一个安全问题。 xyman 提供了类似的解决方案,只为给定的主机发送 cookie:***.com/a/37478166/1220560 如何获取其他类的cookie值【参考方案4】:

compile "com.squareup.okhttp3:okhttp-urlconnection:3.8.1" 添加到您的 build.gradle。

然后添加

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

 OkHttpClient defaultHttpClient = new OkHttpClient.Builder()
                                 .cookieJar(new JavaNetCookieJar(cookieManager))
                                 .build()

在没有添加任何第三方依赖项的情况下帮助了我,除了来自 OkHttp 的依赖项。

【讨论】:

我之前使用github.com/franmontiel/PersistentCookieJar 的方法在一个客户端的防火墙上失败了。我改用这种方法。成功了!【参考方案5】:

这是 OkHttp3 的 CookieJar 实现。如果您有多个 OkHttp3 实例(通常您应该只有一个实例并将其用作单例),您应该为所有 http 客户端设置相同的 cookiejar 实例,以便它们可以共享 cookie !!!这个实现不是持久化 cookie(它们都将在应用程序重启时被丢弃),但它应该很容易实现 SharedPreferences 持久性而不是内存中的 cookie 列表(cookieStore)。

    CookieJar cookieJar = new CookieJar() 
        private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) 
            cookieStore.put(url.host(), cookies);
        

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) 
            List<Cookie> cookies = cookieStore.get(url.host());
            return cookies != null ? cookies : new ArrayList<Cookie>();
        
    ;
    OkHttpClient httpClient = new OkHttpClient.Builder()
            .cookieJar(cookieJar)
            .build();

【讨论】:

需要注意的是,这个解决方案只支持最基本的情况:网站为自己设置cookies。例如,第 3 方 cookie 将不起作用。 在存储 cookie 时至少使用 cookie.domain() 以确保它们可以再次被正确读取。【参考方案6】:

我为 okhttp3 和 retrofit.2 使用了 franmontiel PeristentCookieJar 库。这种方法的好处是:不需要操纵你的 okhttp 请求。只需在创建改造时设置 cookie 或会话

1.首先将此添加到您的 build.gradle(projectname)
 allprojects 
     repositories 
         jcenter()
         maven  url "https://jitpack.io" 
     
 
2. 将此添加到您的 build.gradle
    compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
3. 像这样构建改造
public static Retrofit getClient(Context context) 

            ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .cookieJar(cookieJar)
                    .build();
            if (retrofit==null) 
                retrofit = new Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(okHttpClient)
                        .build();
            
            return retrofit;
        

【讨论】:

但是当我杀死应用程序时,应用程序需要再次登录,因为我们没有在本地保存。请让我摆脱这个问题。 @Peter 我认为您有 2 个解决方案:1-您可以保存登录结果(例如在 sharePrefrences 中),然后在下一次午餐时使用它。 2-在每次登录时在后台调用您的登录名(因此您必须将登录用户信息保存在 sharePrefrences 中并以此调用登录名)。我提到的库对于像浏览器一样保存 cookie 很有用。例如,当您登录网站时,您无需再次登录即可访问所有页面。 如何在 SharedPrefence 中保存和存储。我刚刚使用了以下几行 ClearableCookieJar cookieHandler = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context)); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient httpClient = new OkHttpClient.Builder() .cookieJar(cookieHandler) .addInterceptor(logging) .build(); 我已经在第一个选项的帮助下进行了处理,但是当我尝试调用任何其他 API 时,我需要从应用程序中强制注销而不是登录,因为改造也更改了 phpSESSIONID 和默认 cookie。【参考方案7】:

您可以在 Kotlin 中尝试:

val cookieJar = JavaNetCookieJar(CookieManager())
val url = "https://www.google.com/"

val requestBuilder = Request
        .Builder()
        .url(url)
        .get()

val request = requestBuilder
        .build()

val response = OkHttpClient.Builder()
        .cookieJar(cookieJar)
        .build()
        .newCall(request)
        .execute()

Gradle

dependencies 
    implementation("com.squareup.okhttp3:okhttp:4.2.2")
    implementation("com.squareup.okhttp3:okhttp-urlconnection:4.2.2")

【讨论】:

【参考方案8】:

第一次运行后保留 cookie 的最小解决方案

public class SharedPrefCookieJar implements CookieJar 

    Map<String, Cookie> cookieMap = new HashMap();
    private Context mContext;
    private SharedPrefsManager mSharedPrefsManager;

    @Inject
    public SharedPrefCookieJar(Context context, SharedPrefsManager sharedPrefsManager) 
        mContext = context;
        mSharedPrefsManager = sharedPrefsManager;
        cookieMap = sharedPrefsManager.getCookieMap(context);
        if (cookieMap == null) 
            cookieMap = new HashMap<>();
        
    

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) 

        for (Cookie cookie : cookies) 
            cookieMap.put(cookie.name(), cookie);
        
        mSharedPrefsManager.setCookieMap(mContext, cookieMap);
    

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) 
        List<Cookie> validCookies = new ArrayList<>();
        for (Map.Entry<String, Cookie> entry : cookieMap.entrySet()) 
            Cookie cookie = entry.getValue();
            if (cookie.expiresAt() < System.currentTimeMillis()) 

             else 
                validCookies.add(cookie);
            
        
        return validCookies;
    


用匕首

@Module
public class ApiModule 


    @Provides
    @Singleton
    InstagramService provideInstagramService(OkHttpClient client)
    
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(InstagramService.BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        InstagramService instagramService = retrofit.create(InstagramService.class);
        return instagramService;
    


    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient(SharedPrefCookieJar sharedPrefCookieJar)
        OkHttpClient client;
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if(BuildConfig.DEBUG)
        
            HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder
                .addInterceptor(httpLoggingInterceptor)
                .addInterceptor(new InstagramHeaderInterceptor())
                .addNetworkInterceptor(new LoggingInterceptor());

        
        client = builder.cookieJar(sharedPrefCookieJar).build();
        return client;
    

【讨论】:

【参考方案9】:

我使用了@gncabrera 的解决方案,但还创建了一个帮助类来帮助进行初始化,并让跨应用程序共享 CookieJar 变得容易。

public class OkHttpClientCreator 

    private static CookieJar mCookieJar;

    public static OkHttpClient.Builder getNewHttpClientBuilder(boolean isDebug, boolean useCookies) 
        if (mCookieJar == null && useCookies) 
            mCookieJar = new BasicCookieJar();
        
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (useCookies) 
            builder.cookieJar(mCookieJar);
        
        if (isDebug) 
            builder.addInterceptor(new LoggingInterceptor());
        
        return builder;
    

    public static OkHttpClient getNewHttpClient(boolean isDebug, boolean useCookies) 
       return getNewHttpClientBuilder(isDebug, useCookies).build();
    


日志拦截器在调试模式下用于打印请求信息并共享cookie jar 实例。跨调用者,以便如果请求需要使用通用的 cookie 处理程序,它们可以。这些 cookie 不会在应用程序启动时持续存在,但这不是我的应用程序的要求,因为我们使用基于令牌的会话,并且 cookie 的唯一需要是登录和生成令牌之间的时间很短。

注意: BasicCookieJar 与 gncabrera 的 MyCookieJar 实现完全相同

【讨论】:

【参考方案10】:

将实现“com.squareup.okhttp3:okhttp-urlconnection:3.8.1”添加到您的 build.gradle。

var interceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
var cookieHandler: CookieHandler = CookieManager()

private var retrofit: Retrofit? = null
retrofit = Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .client(client)
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .build()

private val client : OkHttpClient
            private get() 
                val builder = OkHttpClient.Builder()
                builder.addNetworkInterceptor(interceptor)
                builder.cookieJar(JavaNetCookieJar(cookieHandler))
                builder.connectTimeout(15, TimeUnit.SECONDS)
                builder.readTimeout(20, TimeUnit.SECONDS)
                builder.writeTimeout(20, TimeUnit.SECONDS)
                builder.retryOnConnectionFailure(true)
                return builder.build()
            

【讨论】:

【参考方案11】:

如果你使用带有 dagger 或 hilt 的 Kotlin 并注入你的 Okhttp,你可以添加

implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.0'

感觉版本应该和你的okhttpclient版本一样。

@Provides
@Singleton
fun providesOkHTTPClient(application: Application): OkHttpClient 
    val cookieManager = CookieManager()
    cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)
    
    return OkHttpClient.Builder()
        .cookieJar(JavaNetCookieJar(cookieManager))
        .addInterceptor(
            ChuckInterceptor(application)
                .showNotification(true)
                .retainDataFor(ChuckInterceptor.Period.ONE_DAY)
        ).addInterceptor(
            HttpLoggingInterceptor().setLevel(
                if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
                else HttpLoggingInterceptor.Level.NONE
            )
        ).build()

记得在提供 okhttpClient 的同一模块(数据)上添加依赖项

【讨论】:

【参考方案12】:

我可以向你展示一个让 OkHttp3 自动处理 cookie 的库。它可以轻松使用。

只需将 cookie 保存在内存中,以便在需要时自己进行持久化。 在 android 和纯 Java 环境下运行。

    String url = "https://example.com/webapi";

    OkHttp3CookieHelper cookieHelper = new OkHttp3CookieHelper();

    OkHttpClient client = new OkHttpClient.Builder()
            .cookieJar(cookieHelper.cookieJar())
            .build();

    Request request = new Request.Builder()
            .url(url)
            .build();

Gradle

compile 'org.riversun:okhttp3-cookie-helper:1.0.0'

Maven

<dependency>
<groupId>org.riversun</groupId>
<artifactId>okhttp3-cookie-helper</artifactId>
<version>1.0.0</version>
</dependency>

【讨论】:

这不是使用来自 cookie 本身的域来存储 cookie - 所以这不会很好。 我上面提到过。

以上是关于使用 OkHttp 3 自动处理 cookie的主要内容,如果未能解决你的问题,请参考以下文章

在 Okhttp 中处理身份验证

如何使用 OkHttp 在 Android 上实现 cookie 处理?

android okhttp3带cookie请求

Android Okhttp完美同步持久Cookie实现免登录

java 使用Retrofit / OkHttp轻松处理Cookies

使用存储在 persistentcookiejar 或其他容器中的 Cookie 来处理 android 中的 okhttp 请求