Moya下的双token代码

前提

虽然大部分情况下服务器给的接口是单token,但是仍然有不少情况下会设计成双token,一个access_token,一个refresh_token。access_token有效期短,网络接口都需要带access_token。只有在access_token失效的情况下才去用刷新token的接口,通过带refresh_token去刷新。

问题

网络请求都是并发的。比如隔了几天,打开首页,要执行A,B,C三个并发网络请求,这个时候access_token超期了,A,B,C都会失败,如果都去请求刷新token的请求D,会请求三次D,三次D的返回的access_token都会不一致,A,B,C以三个不同的access_token去再次请求,服务器可能会认为其中的access_token不正确。

解决方法

采用锁的方式,比如refreshToken()方法用来执行刷新token请求,A,B,C并发去请求refreshToken,采用锁,只有一个能执行refreshToken,获取到新的access_token和refresh_token后缓存到本地,并缓存时间戳token_time,其他的请求从本地缓存中获取token_time,获取的token_time和当前时间戳curTime比较,比如curTime -  token_time < 30s,就认为是有效的时间,取本地缓存的access_token即可。

另外,执行refreshToken()方法过程中,可能会遇到网络请求失败,这个时候可以写个逻辑,执行三次失败情况下认为是网络请求失败。

Moya下具体实现

public class MyMoyaProvider<Target>: MoyaProvider<Target> where Target: Moya.TargetType {

    privateletdisposeBag=DisposeBag()

    private var refreshCount: Int = 0

    private lazy var lock: NSLock = {

        lettheLock =NSLock()

        returntheLock

    }()

    publicoverridefuncrequest(_target:Target, callbackQueue:DispatchQueue? = .none, progress:ProgressBlock? = .none, completion:@escapingCompletion) ->Cancellable{

        returnsuper.request(target, callbackQueue: callbackQueue, progress: progress, completion: { [unownedself] resultin

            switchresult {

            caselet.success(moyaResponse):

                ifmoyaResponse.statusCode==401{

                    ifself.refreshCount>3{

                        self.refreshCount=0

                        KMLogInfo("refreshCount > 3")

                        completion(result)

                        return

                    }else{

                        self.refreshCount=self.refreshCount+1

                    }

                    ifmoyaResponse.request?.allHTTPHeaderFields?.index(forKey:"Authorization")!=nil{

                        self.lock.lock()

                        KMLogInfo("objc_sync_enter")

                        // self.semaphore.wait()

                        letrefreshToken =Profile.getRefreshToken()

                        ifrefreshToken==""{

                            KMLogInfo("refreshToken is nil")

                            KMLogInfo("objc_sync_exit")

                            self.lock.unlock()

                            return

                        }

                        letsaveTokenTime =Profile.getSaveAccessTokenTime()

                        letcurTime =Date().timeIntervalSince1970

                        KMLogInfo("saveTokenTime:\(saveTokenTime), curTime: \(curTime)")

                        if(curTime-saveTokenTime) <30{

                            // 表示已经是最新的token

                            self.refreshCount=0

                            self.request(target, callbackQueue: callbackQueue, progress: progress, completion: completion)

                            // self.semaphore.signal()

                            KMLogInfo("objc_sync_exit")

                            self.lock.unlock()

                        }else{

                            // 需要去重新请求token

                            // let access_token = Profile.getAccessToken()

                            // 加锁

                            // objc_sync_enter(self)

                            letparameters:Dictionary= ["refresh_token": refreshToken]as[String:Any]

                            // 异步转同步

                            // let sema = DispatchSemaphore(value: 1)

                            NetworkProvider.rx.request(.refreshToken(paras: parameters)).filterSuccessfulStatusCodes().mapJSON().asObservable().mapObject(type:KMLoginModel.self).subscribe(onNext: { [unownedself] elementin

                                KMLogInfo("refreshToken success")

                                Profile.saveAccessToken(token: element.access_token??"")

                                Profile.saveRefreshToken(token: element.refresh_token??"")

                                Profile.setSaveAccessTokenTime(saveTokenTime:Date().timeIntervalSince1970)

                                self.refreshCount=0

                                KMLogInfo("objc_sync_exit")

                                self.lock.unlock()

                                self.request(target, callbackQueue: callbackQueue, progress: progress, completion: completion)

                            }, onError: {_in

                                // YBProgressHUD.showTipMessage(text: "您的账号权限过期,请重新登录!")

                                KMLogInfo("refreshToken fail")

                                KMProgressHUD.shareInstance.showHUDAutoHide(message:"您的账号权限过期,请重新登录")

                                // 请求错误,直接回到登录页

                                // NotificationCenter.default.post(name: Notification.Name(rawValue: "NOTIFICATION_LOGOUT"), object: nil)

                                // 清空本地token信息

                                completion(result)

                                letappDelegate = (UIApplication.shared.delegate)as!AppDelegate

                                appDelegate.logout()

                                KMLogInfo("objc_sync_exit")

                                self.lock.unlock()

                                // cancelBlock(result)

                            }, onCompleted:nil, onDisposed:nil).disposed(by:self.disposeBag)

                        }

                        // self.semaphore.wait()

                    }

                }

            caselet.failure(error):

                KMLogInfo(error.localizedDescription)

            }

            completion(result)

        })

    }

}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容