Я использую аутентификацию на основе токенов с токенами jwt.Токен доступа имеет короткое время жизни, токен обновления может истечь или может быть отозван.
Процедура обновления токена доступа осуществляется в соответствии с рекомендациями Siesta.Процедура отзыва токена Refresh является частью первой.Все работает нормально, если у меня нет нескольких одновременных запросов от моего приложения iOS к серверу.Если у более чем одного из этих запросов истек срок действия маркера доступа, первый запрос успешно обновит его.Но второй не удастся, потому что он предоставит тот же токен обновления, который уже использовался первым запросом.Чтобы преодолеть это, я использую простую охрану в процедуре цепочки запросов.Вопрос в том, будет ли request.repeated()
перезапускать декораторы каждый раз и предоставлять новый токен доступа к запросу, или же он не будет работать так же, как я описал выше, когда флаг _tokenRefreshInProgress
сбрасывается?
См. Некоторые вопросы вкод комментария
service.configure(whenURLMatches: { $0 != self.authRefreshResource.url }) {
if let session = self.appSession {
$0.headers["Authorization"] = "Bearer \(session.tokens.access)"
}
$0.decorateRequests {
self.globalApi401Handler(request: $1)
}
}
service.configure(authRefreshResourceRoute) {
if let session = self.appSession {
$0.headers["Authorization-Refresh"] = "Bearer \(session.tokens.refresh)"
}
$0.decorateRequests {
self.refreshToken409Handler(request: $1)
}
}
private func globalApi401Handler(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!")
if self._tokenRefreshInProgress { //Should I use semaphore or other thread sync primitive here?
return .passTo(request.repeated()) //Another request is already trying to refresh. Retry until guard is cleared. Do we get new access token from decorators here?
} else {
self._tokenRefreshInProgress=true //set guard for other requests
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, // Did request fail…
error.httpStatusCode == 401, // …because of expired token?
self.appSession == nil || self.relogin == true { // appSession is new
self.onEvent.trigger(.Relogin)
return .useThisResponse
}
#warning("Use custom error unwrapper to conform API error reporting convention")
if case .failure(let error) = $0.response {
log.error("Got error: \(error.httpStatusCode ?? -1), unwrapping data...")
if let data = error.entity?.content as? Data {
log.error("Error data is: \(String(decoding: data, as: UTF8.self))")
} else {
log.error("Error data cannot be unwrapped")
}
}
return .useThisResponse // If not, use the response we got.
}
}