网络请求时关于cookie或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是否失效那儿的判断可以自行处理,比如说用户是否登录?这样的判断。

### 高并发场景下的Cookie失效解决方案 在高并发环境下处理Cookie失效问题至关重要,这不仅影响用户体验还可能引发安全风险。以下是几种有效的解决方案: #### 1. 使用无状态会话管理 通过移除服务器端存储来减少因高并发带来的压力。可以采用JWT(JSON Web Token)替代传统Session机制[^1]。 ```python import jwt def create_token(user_id): payload = {"user": user_id} token = jwt.encode(payload, 'secret', algorithm='HS256') return token.decode('utf-8') def verify_token(token): try: decoded = jwt.decode(token, 'secret', algorithms=['HS256']) return True, decoded['user'] except Exception as e: return False, None ``` #### 2. 实施分布式缓存策略 利用Redis等内存级数据结构存储库作为临容器保存用户的认证信息,当检测到cookie过期快速验证并更新新token给客户端。 ```bash redis-cli SETEX session:<session-id> <ttl-in-second> "<json-payload>" ``` #### 3. 增加Token刷新逻辑 允许前端定期向后端发送请求以延长当前登录的有效期限,在接近超自动触发续签操作而不打断用户交互流程。 ```javascript function refreshToken() { fetch('/api/refresh-token', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: localStorage.getItem('authToken')}) }) .then(response => response.json()) .then(data => { if (data.newToken) { localStorage.setItem('authToken', data.newToken); } }); } setInterval(refreshToken, 30 * 60 * 1000); // 每半小执行一次 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值