OkHttp 4.9.0登录后Cookie未在后续请求中加载的问题
我使用OkHttp 4.9.0开发SonarApi相关功能,登录请求成功后,服务器返回了XSRF-TOKEN和JWT-SESSION的Set-Cookie响应头,但后续调用updateRuleMarkdownNote接口的请求未携带这些Cookie,导致返回401未授权状态码。相关代码及请求日志如下:
相关代码
private OkHttpClient http; public SonarApi(String serverUrl, String secret) { HttpLoggingInterceptor it = new HttpLoggingInterceptor(); it.setLevel(Level.BODY); CookieManager cookieManager = new CookieManager(); cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); JavaNetCookieJar cookieJar = new JavaNetCookieJar(cookieManager); http = new OkHttpClient.Builder().addInterceptor(it).cookieJar(cookieJar).build(); } public void login(String userName, String password) throws IOException { RequestBody formBody = new FormBody.Builder().add("login", userName).add("password", password).build(); Request req = new Request.Builder() .addHeader("Accept", "application/json") .addHeader("Host", "x.x.x.x:9876") .addHeader("Origin", serverUrl) .addHeader("Content-Type", "application/x-www-form-urlencoded") .url(serverUrl + "/api/authentication/login").post(formBody).build(); http.newCall(req).execute(); } public boolean updateRuleMarkdownNote(String ruleKey, String mdNote) throws IOException { RequestBody formBody = new FormBody.Builder().add("key", ruleKey).add("markdown_note", mdNote).build(); Request req = new Request.Builder().addHeader("Content-Type", "application/x-www-form-urlencoded").url(serverUrl + "/api/rules/update").post(formBody).build(); int code = http.newCall(req).execute().code(); return code >= 200 && code < 300; } public static void main(String[] args) { SonarApi sonarApi = new SonarApi("http://x.x.x.x:9876", "cac3b3c65347e87cdf1f9a7352935db79e2435f5"); try { sonarApi.login("admin", "******"); sonarApi.updateRuleMarkdownNote("custom-rules-java:ExampleRules01", "*foo*"); } catch (IOException e) { e.printStackTrace(); } }
请求日志
六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: --> POST http://172.25.160.238:9876/api/authentication/login 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Length: 34 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Accept: application/json 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Host: 172.25.160.238:9876 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Origin: http://172.25.160.238:9876 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Type: application/x-www-form-urlencoded 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: login=admin&password=%23Fgglgy0223 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: --> END POST (34-byte body) 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: <-- 200 http://172.25.160.238:9876/api/authentication/login (266ms) 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-Frame-Options: SAMEORIGIN 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-XSS-Protection: 1; mode=block 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-Content-Type-Options: nosniff 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Set-Cookie: XSRF-TOKEN=5iohli0jdhtla6tk8a87598log; Max-Age=259200; Expires=Mon, 19-Jun-2023 06:44:23 GMT; Path=/ 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Set-Cookie: JWT-SESSION=eyJhbGciOiJIUzI1NiJ9.eyJsYXN0UmVmcmVzaFRpbWUiOjE2ODY4OTc4NjMyNTMsInhzcmZUb2tlbiI6IjVpb2hsaTBqZGh0bGE2dGs4YTg3NTk4bG9nIiwianRpIjoiQVlqQzhxcFNkRWtsQ2VJdXhIQ0IiLCJzdWIiOiJBWHd3MUhOY1NCN0xfa1UtMEhydyIsImlhdCI6MTY4Njg5Nzg2MywiZXhwIjoxNjg3MTU3MDYzfQ.sDbWX2_pNGIAIMjrgOBSyEnsBLc9fncz2-7XS_uyx-M; Max-Age=259200; Expires=Mon, 19-Jun-2023 06:44:23 GMT; Path=/; HttpOnly 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Length: 0 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Date: Fri, 16 Jun 2023 06:44:23 GMT 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Keep-Alive: timeout=60 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Connection: keep-alive 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: <-- END HTTP (0-byte body) 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: --> POST http://172.25.160.238:9876/api/rules/update 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Length: 58 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Type: application/x-www-form-urlencoded 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: key=custom-rules-java%3AExampleRules01&markdown_note=*foo* 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: --> END POST (58-byte body) 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: <-- 401 http://172.25.160.238:9876/api/rules/update (7ms) 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-Frame-Options: SAMEORIGIN 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-XSS-Protection: 1; mode=block 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: X-Content-Type-Options: nosniff 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Content-Length: 0 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Date: Fri, 16 Jun 2023 06:44:23 GMT 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Keep-Alive: timeout=60 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: Connection: keep-alive 六月 16, 2023 2:42:54 下午 okhttp3.internal.platform.Platform log 信息: <-- END HTTP (0-byte body)
问题原因及解决方案
问题根源
- 域名不匹配导致Cookie未被关联:登录请求中手动设置的
Host头为x.x.x.x:9876,但实际请求的服务器地址是http://172.25.160.238:9876。Java原生CookieManager严格遵循RFC规范匹配Cookie域名,服务器返回的Cookie绑定在172.25.160.238上,手动设置的Host头让CookieManager误以为请求域名是x.x.x.x,导致Cookie无法被正确关联到后续请求的域名。 - JavaNetCookieJar的严格匹配逻辑:
JavaNetCookieJar依赖原生CookieManager,其对Cookie的存储和携带逻辑要求域名完全匹配,一旦域名不一致,就不会在后续请求中自动携带Cookie。
解决办法
方法一:移除手动设置的Host头(最简单有效)
直接删除login方法中.addHeader("Host", "x.x.x.x:9876")这一行,让OkHttp自动根据请求URL生成正确的Host头,CookieManager就能正确识别域名并存储、携带Cookie。修改后的登录方法:
public void login(String userName, String password) throws IOException { RequestBody formBody = new FormBody.Builder() .add("login", userName) .add("password", password) .build(); Request req = new Request.Builder() .addHeader("Accept", "application/json") .addHeader("Origin", serverUrl) .addHeader("Content-Type", "application/x-www-form-urlencoded") .url(serverUrl + "/api/authentication/login") .post(formBody) .build(); http.newCall(req).execute(); }
方法二:替换为OkHttp内置的CookieJar实现(推荐)
使用OkHttp官方提供的PersistentCookieJar替代Java原生的CookieManager,它的域名匹配逻辑更灵活,适配OkHttp的请求流程。
首先添加Maven依赖:
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp-urlconnection</artifactId> <version>4.9.0</version> </dependency>
然后修改SonarApi构造方法:
private OkHttpClient http; public SonarApi(String serverUrl, String secret) { HttpLoggingInterceptor it = new HttpLoggingInterceptor(); it.setLevel(Level.BODY); // 内存缓存Cookie,需持久化可自定义实现 CookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context)); http = new OkHttpClient.Builder() .addInterceptor(it) .cookieJar(cookieJar) .build(); }
注:桌面应用不适用SharedPrefsCookiePersistor,可仅使用SetCookieCache实现内存级Cookie存储,或自定义文件持久化逻辑。
方法三:确保Host头与URL域名一致
若必须手动设置Host头,需保证其值与请求URL的域名完全一致。比如服务器实际地址是172.25.160.238:9876,则Host头应设为172.25.160.238:9876,而非x.x.x.x:9876。
内容的提问来源于stack exchange,提问作者WestFarmer
相关产品推荐
相关产品推荐

