RxSwift 更新身份验证令牌
Posted
技术标签:
【中文标题】RxSwift 更新身份验证令牌【英文标题】:RxSwift renew Authentication Token 【发布时间】:2019-04-17 18:33:14 【问题描述】:我正在尝试使用 Moya 和 RxSwift 提出一个解决方案,以更新身份验证令牌并重试请求。
问题是我有多个请求同时进行,所以假设在身份验证令牌过期时触发了 10 个请求,我将尝试更新所有请求的令牌,并且一旦第一个更新其他的会失败,因为他们使用了错误的令牌来更新。
我想做的只是建立一个请求队列(也许),然后重试这些请求。不确定这是否是最好的方案。
这是我目前所拥有的:
final class NetworkOnlineProvider
fileprivate let database = DatabaseClient(database: DatabaseRealm()).database
fileprivate let provider: MoyaProvider<NetworkAPI>
init(endpointClosure: @escaping MoyaProvider<NetworkAPI>.EndpointClosure = MoyaProvider<NetworkAPI>.defaultEndpointMapping,
requestClosure: @escaping MoyaProvider<NetworkAPI>.RequestClosure = MoyaProvider<NetworkAPI>.defaultRequestMapping,
stubClosure: @escaping MoyaProvider<NetworkAPI>.StubClosure = MoyaProvider.neverStub,
manager: Manager = MoyaProvider<NetworkAPI>.defaultAlamofireManager(),
plugins: [PluginType] = [],
trackInflights: Bool = false)
self.provider = MoyaProvider(endpointClosure: endpointClosure, requestClosure: requestClosure, stubClosure: stubClosure, manager: manager, plugins: plugins, trackInflights: trackInflights)
fileprivate func getJWTRenewRequest() -> Single<Response>?
if let token = JWTManager.sharedInstance.token
return provider.rx.request(.renew(token: token))
return nil
func tokenRequest() -> Single<String>
let errorSingle = Single<String>.create single in
single(.error(APIError.failure))
return Disposables.create()
let emptyJWTSingle = Single<String>.create single in
single(.success(""))
return Disposables.create()
// Return if no token found
guard let appToken = JWTManager.sharedInstance.getJWT() else
return refreshToken() ?? emptyJWTSingle
// If we have a valid token, just return it
if !appToken.hasTokenExpired
return Single<String>.create single in
single(.success(appToken.token))
return Disposables.create()
// Token has expired
let newTokenRequest = refreshToken()
return newTokenRequest ?? errorSingle
func refreshToken() -> Single<String>?
return getJWTRenewRequest()?
.debug("Renewing JWT")
.filterSuccessfulStatusCodes()
.map (response: Response) -> (token: String, expiration: Double) in
guard let json = try? JSON(data: response.data) else throw RxError.unknown
let success = json["success"]
guard
let jwt = success["jwt"].string,
let jwt_expiration = success["jwt_expiration"].double,
let valid_login = success["valid_login"].bool, valid_login
else throw RxError.unknown
return (token: jwt, expiration: jwt_expiration)
.do(onSuccess: (token: String, expiration: Double) in
JWTManager.sharedInstance.save(token: JWT(token: token, expiration: String(expiration)))
)
.map (token: String, expiration: Double) in
return token
.catchError e -> Single<String> in
print("Failed to Renew JWT")
JWTManager.sharedInstance.delete()
UIApplication.shared.appDelegate.cleanPreviousContext(jwt: true)
let loginVC = UIStoryboard(storyboard: .login).instantiateViewController(vc: LoginViewController.self)
UIApplication.shared.appDelegate.window?.setRootViewController(UINavigationController(rootViewController: loginVC))
throw e
func request(_ target: NetworkAPI) -> Single<Response>
let actualRequest = provider.rx.request(target)
if target.isAuthenticatedCall
return tokenRequest().flatMap _ in
actualRequest
return actualRequest
【问题讨论】:
【参考方案1】:解决办法在这里:RxSwift and Retrying a Network Request Despite Having an Invalid Token
关键是使用 flatMapFirst,这样您只需对第一个 401 发出一个请求,而在该请求进行中时忽略其他 401。
与文章相关的要点包括证明它有效的单元测试。
【讨论】:
以上是关于RxSwift 更新身份验证令牌的主要内容,如果未能解决你的问题,请参考以下文章
Symfony 2.1.7 - 用户通过身份验证后更新安全令牌设置特定角色