我正在尝试将Siesta swift包与服务器上的API一起使用。我们已使用访问和刷新令牌设置 JWT 身份验证。我们可以通过刷新过程成功进行身份验证并获取新的访问令牌。但是我们制定的解决方案看起来有点笨拙。
我们正在使用这样的请求装饰器
func init() {
service.configure("**") {
if let session = self.appSession {
$0.headers["Authorization"] = "Bearer (session.tokens.access)"
}
$0.decorateRequests {
self.globalApiFailHandler(request: $1)
}
}
service.configure(authRefreshResource) {
if let session = self.appSession {
$0.headers["Authorization-Refresh"] = "Bearer (session.tokens.refresh)"
}
$0.decorateRequests {
self.refreshTokenFailure(request: $1)
}
}
}
private func globalApiFailHandler(request: Siesta.Request) -> Request {
return request.chained { //special case to Refresh Token On Auth Failure
if case
.failure(let error) = $0.response, // Did request fail…
error.httpStatusCode == 401, // …because of expired token?
self.appSession != nil { // we have refreshToken
log.warning("Seems like Access Token is expired, Trying to refresh it!")
return .passTo(
self.refreshAuth().chained { // first request a new token, then:
if case .failure = $0.response { // If token request failed…
return .useThisResponse // …report that error.
} else {
return .passTo(request.repeated()) // We have a new token! Repeat the original request.
}
}
)
}
if case
.failure(let error) = $0.response,
error.httpStatusCode != 409 {
log.warning("Something went wrong during request: (error)")
self.retryLaterEvent() // TODO: Really need this here?
}
return .useThisResponse // If not, use the response we got.
}
}
private func refreshTokenFailure(request: Siesta.Request) -> Request {
return request.chained {
if case
.failure(let error) = $0.response { // Did request fail…
log.error("Refresh token procedure failed with (error).")
if error.httpStatusCode == 409 {
log.warning("409, Resetting app session storage! Relogin or app recreation is needed!")
self.relogin = true //Reset saved sessions to create new app
self.reloginEvent()
} else {
log.warning("Something went wrong during refresh token procedure. Please retry later!")
self.retryLaterEvent()
}
///let requestError = RequestError(userMessage: "Unable to refresh access token", cause: "")
let response = Response.failure(error) //(requestError)
let responseInfo = ResponseInfo(response: response)
return .useResponse(responseInfo) // If not, use the response we got.
}
return .useThisResponse // We have new token!
}
}
请注意409
返回代码签入globalApiFailHandler
。它之所以存在,是因为全球装饰器总是需要authRefreshResource
。如果我们省略该检查,API 将在某些服务器错误下卡在无限循环刷新令牌中。
问题是如何为我们想要的特定资源禁用全局装饰器?这样做将优雅地解决我们的问题。
你可以将任意谓词传递给configure(whenURLMatches:)
,这让你可以通过手术排除任何你喜欢的东西:
service.configure(whenURLMatches: { url in url.path != "/auth" }) {
...
}
或者,如果您像您一样,想要排除您手边有Resource
的 URL:
service.configure(whenURLMatches: { $0 != authRefreshResource.url }) {
...
}