网络请求时关于cookie或token失效的解决方案

探讨了在使用RxJava进行网络请求时如何处理Token过期的情况,包括使用Intercept和retryWhen操作符来实现Token刷新,以及如何避免同步请求带来的问题。

当一次网络请求(比如说请求购物车的数据,这时是需要验证用户身份的标识的,例如cookie或者token)
想到的三种方法:
1.最开始没用rxjava之前就是用的这种,但是感觉实在累赘。当token失效后重新请求登录接口,当登录成功后通知原先的Activity重新加载数据。这样需要对每个接口都进行token是否失效的判断。

2.使用Intercept(参考这篇文章,但是Okhttpclien3.0删除了ErrorHandler)onErrorResumeNext操作符实现app与服务器间token机制
http://blog.youkuaiyun.com/johnny901114/article/details/51533586),在intercept方法中拿到返回的json字符串,然后判断token是否失效,如果失效,那么重新登录,但是这儿需要注意的是因为需要继续往下传递请求,登录接口的请求必须是同步的!(ps:后来朋友想了另一个办法,在intercept中抛出异常,这儿就需要用到第三种方法了)

3.使用retryWhen操作符
(关于retryWhen这篇博客讲的非常好http://www.jianshu.com/p/023a5f60e6d0
最开始我的理解有问题。我的代码是这样的。

ApiClient.getInstance()
    .getUserCourse(cookies, "1", "1")
                .flatMap(new Func1<MyCourse, Observable<MyCourse>>() {
                    @Override
                    public Observable<MyCourse> call(MyCourse myCourse) {
                        if (myCourse.status == 205) {
                            return Observable.error(new Exception("kkkk"));
                        }
                        return Observable.just(myCourse);
                    }
                })
                .retryWhen(new RetryWithDelay(3, 1000))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<MyCourse>() {
                    @Override
                    public void call(MyCourse response) {
                        fillData(response);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable)                    Log.i("===========k",throwable.toString());
                    }
                });

然后我在retryWhen中进行了重新登录获取到了最新的cookie,结果显示没有获取到正确的数据,我猜,难道retryWhen只重试了flatMap?当然不是,我抓包得到的结果是,重新进行了网络请求,但是并没有使用新的cookie,为什么呢,cookies作为一个成员变量,他的值变化了啊!
然后我写了个just的例子测试了下!

str = "aaa";
        Observable.just(str).map(new Func1<String, String>() {
            @Override
            public String call(String s) {
                Log.i("====", "s == " + s);
                if ("aaa".equals(s)) throw new RuntimeException(s);
                return s + "123";
            }
        }).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
            @Override
            public Observable<?> call(Observable<? extends Throwable> observable) {
                return observable.zipWith(Observable.range(1, 4), new Func2<Throwable, Integer, Integer>() {
                    @Override
                    public Integer call(Throwable throwable, Integer i) {
                        str = "ggg";
                        return i;
                    }
                }).flatMap(new Func1<Integer, Observable<? extends Long>>() {
                    @Override
                    public Observable<? extends Long> call(Integer retryCount) {
                        return Observable.timer(1, TimeUnit.SECONDS);
                    }
                });
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                Log.i("====k", "s = " + s);
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                Log.i("====", "throwable = " + throwable.getMessage());
            }
        });

结果是
aaa
aaa
aaa

what?
为啥啊?为什么后面不打印ggg呢?
看这里吧。
关于retryWhen的issue
https://github.com/ReactiveX/RxJava/issues/4840
也就说retryWhen每次重试的都是Source Observable!为了保证使用最新的cookie,使用defer操作符,原理类似于fromCallable.
再来说第二条提到的那个抛出异常的方法。
具体实现就是Intercept和RetryWhen结合,在Intercept中进行token是否失效的判断,如果token失效那么就直接抛出异常,然后在retryWhen中进行重新登录,并给token设置最新的值。这样就避免了同步请求的问题。不过需要注意:

1.因为重新登录是异步请求,所以需要对retryWhen中的重试进行限制,即重新请求原先接口需要延迟(用timer操作符)

2.对重新登录次数进行限制
3.最好自定义抛出的异常,这样方便在Subscriber的onError方法或者retryWhen中进行判断是否是token失效,万一后续还有其他问题需要在Intercept中处理呢。

4.最大的弊端是对所有的接口都进行了token是否失效的判断(因为Intercept会是全局的),所以在Intercept中对token是否失效那儿的判断可以自行处理,比如说用户是否登录?这样的判断。

<think>首先,用户的问题是关于JMeter的HTTP Cookie管理器没有在请求中发送Token问题。用户说:“使用 HTTP Cookie 管理器后请求未携带 Token问题”,意思是尽管使用了Cookie管理器,Token没有被发送。 我的回答需要基于系统指令: - 所有行内数学表达式必须使用$...$格式。但这里可能没有数学表达式,所以我可以忽略。 - 独立公式必须使用$$...$$格式并单独成段。同样,可能不需要。 - LaTeX语法正确。确保任何数学格式正确。 - 使用中文回答。 - 生成相关问题。在回答结束,添加相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。用户提供了引用[1]到[4],我需要参考这些引用。 用户提供的引用: - [^1]: 关于远程服务测试的,提到使用工具发送HTTP请求和验证响应。 - [^2]: 关于REST web服务是无状态的,使用会话Token进行认证,Token不应在URL中发送,应有过期日期等。 - [^3]: JMeter和LoadRunner的对比,提到JMeter的安装、协议支持、分布式测试等。 - [^4]: 一些HTTP客户端库的列表。 用户的问题是关于JMeter的Cookie管理器不发送Token。引用[^2]特别相关,因为它讨论了Token的认证。 用户的需求:解决JMeter中HTTP Cookie管理器不发送Token问题。 我的回答结构: 1. 帮助用户逐步解决问题。 2. 确保回答真实可靠。 3. 参考提供的引用。 步骤: - 理解问题:JMeter的HTTP Cookie管理器应该自动管理Cookie,包括Token。但用户说它没有发送。 - 可能的原因: - Cookie管理器配置错误。 - Token没有被正确存储在Cookie中。 - 请求中缺少必要的头参数。 - 解决方案: - 检查Cookie管理器的设置。 - 确保Token在响应中被正确设置。 - 使用其他组件如HTTP Header Manager来手动添加Token。 - 参考引用[^2]:它提到Token应通过会话Token发送,而不是在URL中,并且应有过期日期。这暗示Token可能通过CookieAuthorization头发送。 在回答中,我需要自然地引用引用。例如,在讨论Token认证,可以引用[^2]。 最终,生成相关问题。 回答大纲: 1. **介绍问题**:解释HTTP Cookie管理器的作用,以及为什么Token可能没有被发送。 2. **可能原因**:列出常见原因。 3. **解决方案步骤**:逐步指导如何解决。 4. **验证**:如何确认Token被发送。 5. **相关问题**:在结束添加。 使用中文。 生成相关问题: - 基于用户的问题,例如: 1. JMeter Cookie管理器的其他常见问题? 2. 如何手动添加Token到JMeter请求? 3. JMeter中如何测试Token认证的API? 确保回答清晰、结构化。</think>### 解决 JMeter HTTP Cookie 管理器未发送 Token问题 在使用 JMeter 进行 API 测试,HTTP Cookie 管理器用于自动管理会话 Cookie(包括认证 Token),但有它可能无法正确发送 Token。这通常是由于配置错误环境问题导致。以下我将逐步解释问题原因,并提供详细的解决方案。我的回答基于 JMeter 官方文档和最佳实践[^3],并结合了认证机制的原理[^2]。 #### 问题原因分析 在 JMeter 中,HTTP Cookie 管理器负责从服务器响应中提取 Cookie(如会话 Token),并在后续请求中自动添加。但 Token 未发送的常见原因包括: - **Cookie 提取失败**:服务器响应中未正确设置 Token Cookie Cookie 管理器配置错误。 - **Token 认证机制问题**:Token 可能通过 Authorization 头其他方式发送,而非 Cookie。REST 服务通常是无状态的,Token 应通过安全方式(如 HTTP 头)传递,避免在 URL 中暴露[^2]。 - **环境配置错误**:JMeter 的线程组设置、Cookie 策略协议支持问题(如 HTTPS 处理不当)。 - **并发缓存问题**:测试脚本中的线程冲突浏览器缓存干扰。 引用[^3]指出,JMeter 的脚本开发功能相对较弱,许多参数需手动增强,这可能增加配置复杂度。 #### 解决方案:逐步修复 按照以下步骤操作,确保 Token 被正确发送。建议使用 JMeter 5.6 更高版本。 1. **检查服务器响应,确认 Token 被设置** - 在 JMeter 测试计划中,添加一个 **HTTP 请求** 发送登录请求(例如 POST 到 `/api/login`)。 - 在请求后添加 **查看结果树** 监听器,运行测试并检查响应。 - 如果响应中包含 Set-Cookie 头(如 `Set-Cookie: token=abc123; Path=/; HttpOnly`),说明 Token 被正确设置。 - 如果没有,检查服务器端代码:确保登录 API 返回了有效的 Cookie。参考[^2],Token 应有过期日期并通过安全方式发送。 - 示例响应验证代码(基于引用[^1]): ```java // 示例:验证登录响应是否包含 Token Cookie @Test public void testLoginResponse() { given() .contentType("application/json") .body("{\"username\": \"test\", \"password\": \"pass\"}") .when() .post("/api/login") .then() .statusCode(200) .header("Set-Cookie", containsString("token=")); // 确保 Set-Cookie 存在 } ``` 2. **配置 HTTP Cookie 管理器** - 在测试计划中添加 **HTTP Cookie 管理器**(右键点击线程组 > 添加 > 配置元件 > HTTP Cookie 管理器)。 - 关键设置: - **Cookie Policy**:选择 `standard`(默认) `netscape`,确保兼容大多数服务器。 - **Clear Cookies each iteration**:如果测试需要新会话,取消勾选此项。 - **User-Defined Cookies**:如果 Token 名称不标准(如 `auth_token`),手动添加 Cookie 名称和值(但通常应自动提取)。 - 问题排查:如果 Token 未发送,尝试删除并重新添加 Cookie 管理器,更新 JMeter 版本。 3. **手动添加 Token(备选方案)** 如果 Cookie 管理器失效,使用 **HTTP 头管理器** 手动发送 Token: - 添加 **HTTP 头管理器**(右键点击 HTTP 请求 > 添加 > 配置元件 > HTTP 头管理器)。 - 设置头信息: - 名称:`Authorization`(其他自定义头,如 `X-Auth-Token`)。 - 值:`Bearer ${token_variable}`(需先提取 Token)。 - 提取 Token 方法: - 在登录请求后添加 **正则表达式提取器**(右键点击登录请求 > 添加 > 后置处理器 > 正则表达式提取器)。 - 引用名称:`token` - 正则表达式:`Set-Cookie: token=([^;]+)`(匹配 Cookie 值)。 - 模板:`$1$` - 然后在后续请求的 HTTP 头管理器中引用 `Authorization: Bearer ${token}`。 - 引用[^2]强调,Token 应避免在 URL 中发送,使用头管理器更安全可靠。 4. **验证请求是否携带 Token** - 添加 **查看结果树** 监听器到测试计划。 - 运行测试,检查请求头: - 查找 `Cookie: token=...` `Authorization: Bearer ...`。 - 如果未出现,检查线程组设置:确保所有请求在同一线程组下,以共享 Cookie。 - 使用 **调试采样器** 输出变量:添加一个调试采样器,查看 `${token}` 是否被正确赋值。 5. **常见错误处理** - **HTTPS 问题**:如果 API 使用 HTTPS,在 JMeter 中安装服务器证书(通过 **HTTP 请求默认值** 设置协议为 HTTPS)。 - **并发测试问题**:引用[^3]提到 JMeter 的稳定性稍差,在高并发可能出现 Cookie 丢失。减少线程数使用 **同步定器**。 - **缓存干扰**:在测试计划中添加 **HTTP 缓存管理器** 并禁用缓存。 - **工具限制**:JMeter 对某些协议支持不如 LoadRunner 全面[^3],如果问题持续,尝试使用其他工具如 Postman 验证。 #### 预防措施 - **定期更新 JMeter**:避免旧版本 Bug。 - **参考认证最佳实践**:如引用[^2]所述,使用 OWASP 的 Enterprise Security API 管理 Token。 - **测试脚本优化**:使用 JMeter 插件(如 BlazeMeter)增强脚本录制和参数化。 通过以上步骤,大多数 Token 发送问题都能解决。如果仍失败,请提供更多细节(如 JMeter 日志请求截图),以便进一步诊断。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值