여러 요청을 사용하여 액세스 토큰 새로 고침
악시오 요격기를 작동시키는 데 어려움을 겪고 있어요
토큰이 만료되면 액세스 토큰을 새로 고치고 토큰이 새로 고쳐지면 원래 요청을 재시도해야 합니다.난 이 부분이 작동한다.
문제는 동시 API 호출이 있는 경우 토큰이 처음 비활성화되었을 때만 첫 번째 요청을 재시도한다는 것입니다.
다음은 내 인터셉터 코드입니다.
export default function execute() {
let isRefreshing = false
// Request
axios.interceptors.request.use(
config => {
var token = Storage.getAccessToken() //localStorage.getItem("token");
if (token) {
console.log('Bearer ' + token)
config.headers['Authorization'] = 'Bearer ' + token
}
return config
},
error => {
return Promise.reject(error)
}
)
// Response
axios.interceptors.response.use(
response => {
return response
},
error => {
const originalRequest = error.config
// token expired
if (error.response.status === 401) {
console.log('401 Error need to reresh')
originalRequest._retry = true
let tokenModel = {
accessToken: Storage.getAccessToken(),
client: 'Web',
refreshToken: Storage.getRefreshToken()
}
//Storage.destroyTokens();
var refreshPath = Actions.REFRESH
if (!isRefreshing) {
isRefreshing = true
return store
.dispatch(refreshPath, { tokenModel })
.then(response => {
isRefreshing = false
console.log(response)
return axios(originalRequest)
})
.catch(error => {
isRefreshing = false
console.log(error)
// Logout
})
} else {
console.log('XXXXX')
console.log('SOME PROBLEM HERE') // <------------------
console.log('XXXXX')
}
} else {
store.commit(Mutations.SET_ERROR, error.response.data.error)
}
return Promise.reject(error)
}
)
}
위에 강조 표시된 다른 블록에 무엇이 필요한지 잘 모르겠습니다.
편집:
할 때
return axios(originalRequest)
다른 블록에서는 효과가 있지만 행동에 만족하지 않습니다.기본적으로 토큰이 새로 고쳐질 때까지 모든 요청을 다시 시도합니다.토큰이 새로 고쳐진 후에 한 번만 다시 시도했으면 좋겠어. 어떤 아이디어라도.
감사해요.
토큰을 새로 고치고 보류 중인 요청을 실행할 수 있는 추가 대행 수신기를 사용할 수 있습니다.
이 점에서.countDownLatch
수업이 도움이 될 수 있습니다.여기 요격기 코드 샘플이 있습니다
class AutoRefreshTokenRequestInterceptorSample() : Interceptor {
companion object {
var countDownLatch = CountDownLatch(0)
var previousAuthToken = ""
const val SKIP_AUTH_TOKEN = "SkipAccessTokenHeader"
const val AUTHORIZATION_HEADER = "AUTHORIZATION_HEADER_KEY"
}
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response? {
val request = chain.request()
if (shouldExecuteRequest(request)) {
// Execute Request
val response = chain.proceed(request)
if (!response.isSuccessful) {
// Failed Case
val errorBody = response.peekBody(java.lang.Long.MAX_VALUE).string()
val error = parseErrorModel(errorBody)
// Gives Signal to HOLD the Request Queue
countDownLatch = CountDownLatch(1)
handleError(error!!)
// After updating token values, execute same request with updated values.
val updatedRequest = getUpdatedRequest(request)
// Gives Signal to RELEASE Request Queue
countDownLatch.countDown()
//Execute updated request
return chain.proceed(updatedRequest)
} else {
// success case
return response
}
}
// Change updated token values in pending request objects and execute them!
// If Auth header exists, and skip header not found then hold the request
if (shouldHoldRequest(request)) {
try {
// Make this request to WAIT till countdown latch has been set to zero.
countDownLatch.await()
} catch (e: Exception) {
e.printStackTrace()
}
// Once token is Updated, then update values in request model.
if (previousAuthToken.isNotEmpty() && previousAuthToken != "newAccessToken") {
val updatedRequest = getUpdatedRequest(request)
return chain.proceed(updatedRequest)
}
}
return chain.proceed(request)
}
private fun handleError(error: ErrorDto) {
// update your token as per your error code logic
//Here it will make new API call to update tokens and store it in your local preference.
}
/***
* returns Request object with updated token values.
*/
private fun getUpdatedRequest(request: Request): Request {
var updateAuthReqBuilder: Request.Builder = request.newBuilder()
var url = request.url().toString()
if (url.contains(previousAuthToken.trim()) && previousAuthToken.trim().isNotEmpty()) {
url = url.replace(previousAuthToken, "newAccessToken")
}
updateAuthReqBuilder = updateAuthReqBuilder.url(url)
// change headers if needed
return updateAuthReqBuilder.build()
}
private fun shouldExecuteRequest(request: Request) =
shouldHoldRequest(request) && isSharedHoldSignalDisabled()
/**
* If count down latch has any value then it is reported by previous request's error signal to hold the whole pending chain.
*/
private fun isSharedHoldSignalDisabled() = countDownLatch.count == 0L
private fun shouldHoldRequest(request: Request) = !hasSkipFlag(request) && hasAuthorizationValues(request)
private fun hasAuthorizationValues(request: Request) = isHeaderExist(request, AUTHORIZATION_HEADER)
private fun hasSkipFlag(request: Request) = isHeaderExist(request, SKIP_AUTH_TOKEN)
private fun isHeaderExist(request: Request, headerName: String): Boolean {
return request.header(headerName) != null
}
private fun parseErrorModel(errorBody: String): Error? {
val parser = JsonParser()
// Change this logic according to your requirement.
val jsonObject = parser.parse(errorBody).asJsonObject
if (jsonObject.has("Error") && jsonObject.get("Error") != null) {
val errorJsonObj = jsonObject.get("Error").asJsonObject
return decodeErrorModel(errorJsonObj)
}
return null
}
private fun decodeErrorModel(jsonObject: JsonObject): Error {
val error = Error()
// decode your error object here
return error
}
}
토큰의 스키마(복호화 후)는 알 수 없지만 유지하는 것이 좋은 속성 중 하나는 "expiration_date"입니다.토큰 갱신 시기를 알 수 있는 유효기간이 있다고 합니다.
아키텍처에 대한 이해가 없으면 올바른 솔루션을 알려주기가 어렵습니다.그러나 모든 작업을 수동으로 수행하고 있다고 가정해 보겠습니다.일반적으로 onIdle/onActive는 사용자 세션이 아직 정상인지 여부를 확인하는 것입니다.따라서 토큰 정보를 사용하여 값을 새로 고칠 필요가 있는지 여부를 확인할 수 있습니다.
토큰은 사용자가 항상 활성화되어 있고 기한이 만료될 때(이전 2분 등)에만 갱신해야 하기 때문에 이 프로세스를 이해하는 것이 중요합니다.
저는 이렇게 하고 있습니다.
let isRefreshing = false;
let failedQueue = [];
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
axios.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config;
if (error.response.status === 400) {
// If response is 400, logout
store.dispatch(logout());
}
// If 401 and I'm not processing a queue
if (error.response.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
// If I'm refreshing the token I send request to a queue
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
})
.then(() => {
originalRequest.headers.Authorization = getAuth();
return axios(originalRequest);
})
.catch(err => err);
}
// If header of the request has changed, it means I've refreshed the token
if (originalRequest.headers.Authorization !== getAuth()) {
originalRequest.headers.Authorization = getAuth();
return Promise.resolve(axios(originalRequest));
}
originalRequest._retry = true; // mark request a retry
isRefreshing = true; // set the refreshing var to true
// If none of the above, refresh the token and process the queue
return new Promise((resolve, reject) => {
// console.log('REFRESH');
refreshAccessToken() // The method that refreshes my token
.then(({ data }) => {
updateToken(data); // The method that sets my token to localstorage/Redux/whatever
processQueue(null, data.token); // Resolve queued
resolve(axios(originalRequest)); // Resolve current
})
.catch(err => {
processQueue(err, null);
reject(err);
})
.then(() => {
isRefreshing = false;
});
});
}
return Promise.reject(error);
},
);
같은 문제에 직면한 코드의 앵귤러 버전을 참조해 주세요.여러 가지 어프로치를 변경한 후, 이것이 가장 잘 동작하고 있는 최종 코드입니다.
새로 고침 토큰 제공 후 마지막으로 실패한 요청을 다시 초기화합니다.
언급URL : https://stackoverflow.com/questions/56327800/refreshing-access-token-with-multiple-requests
'programing' 카테고리의 다른 글
php 날짜를 mysql 형식으로 변환 (0) | 2022.10.18 |
---|---|
java.sql.SQLException:jdbc:mysql://localhost:3306/dbname에 적합한 드라이버를 찾을 수 없습니다. (0) | 2022.10.18 |
j회피해야 할 함정 쿼리 (0) | 2022.10.18 |
PHP 어플리케이션에서 MySQL 대신 Redis를 사용해야 할 때는 언제입니까? (0) | 2022.10.18 |
java pojo 클래스, java bean, normal 클래스란 무엇입니까? (0) | 2022.10.18 |