【发布时间】:2022-01-20 12:21:14
【问题描述】:
我有一个带有如下身份验证提供程序的 react-admin。 我想刷新我的令牌,但我不知道该怎么做。 我试图关注这个blog post,但我的身份验证有点不同,我无法让它工作(错误“httpClient(...).then”不是一个函数,其他人让我离开它)。
我可以用更简单的解决方案来制作它,不需要在内存中。我试图调用我的刷新端点来获取我的刷新令牌,但我的调用没有当前令牌。
我刷新令牌的端点是: /auth/jwt/刷新
我需要这样称呼它:
curl -X 'GET' \
'http://localhost:8000/auth/jwt/refresh' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
我的响应正文将是:(我需要将其保存到我的本地存储或内存中)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZTUwZDdhZDctOWE5Ni00NzQyLTgxNWEtZTNmZmJmNGRiMTVjIiwiYXVkIjpbImZhc3RhcGktdXNlcnM6YXV0aCJdLCJleHAiOjE2Mzk4NDE1MDF9.-o2yk56sCj_MZx_VA6PxH7gZ-KKSMmopbDNDiapHmn0",
"token_type": "bearer"
}
我的 inMemoryJWTManager 文件:
const inMemoryJWTManager = () => {
let inMemoryJWT = null;
let isRefreshing = null;
let logoutEventName = 'ra-logout';
let refreshEndpoint = '/auth/jwt/refresh';
let refreshTimeOutId;
const setLogoutEventName = name => logoutEventName = name;
const setRefreshTokenEndpoint = endpoint => refreshEndpoint = endpoint;
// This countdown feature is used to renew the JWT before it's no longer valid
// in a way that is transparent to the user.
const refreshToken = (delay) => {
refreshTimeOutId = window.setTimeout(
getRefreshedToken,
delay * 1000 - 5000
); // Validity period of the token in seconds, minus 5 seconds
};
const abordRefreshToken = () => {
if (refreshTimeOutId) {
window.clearTimeout(refreshTimeOutId);
}
};
const waitForTokenRefresh = () => {
if (!isRefreshing) {
return Promise.resolve();
}
return isRefreshing.then(() => {
isRefreshing = null;
return true;
});
}
// The method make a call to the refresh-token endpoint
// If there is a valid cookie, the endpoint will set a fresh jwt in memory.
const getRefreshedToken = () => {
const request = new Request(refreshEndpoint, {
method: 'GET',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'include',
});
isRefreshing = fetch(request)
.then((response) => {
if (response.status !== 200) {
ereaseToken();
global.console.log(
'Token renewal failure'
);
return { token: null };
}
return response.json();
})
.then(({ token, tokenExpiry }) => {
if (token) {
setToken(token, tokenExpiry);
return true;
}
ereaseToken();
return false;
});
return isRefreshing;
};
const getToken = () => inMemoryJWT;
const setToken = (token, delay) => {
inMemoryJWT = token;
refreshToken(delay);
return true;
};
const ereaseToken = () => {
inMemoryJWT = null;
abordRefreshToken();
window.localStorage.setItem(logoutEventName, Date.now());
return true;
}
// This listener will allow to disconnect a session of ra started in another tab
window.addEventListener('storage', (event) => {
if (event.key === logoutEventName) {
inMemoryJWT = null;
}
});
return {
ereaseToken,
getRefreshedToken,
getToken,
setLogoutEventName,
setRefreshTokenEndpoint,
setToken,
waitForTokenRefresh,
}
};
export default inMemoryJWTManager();
这是我的身份验证提供程序:(已更新,使用 inMemoryJWTManager)
import inMemoryJWTManager from './inMemoryJWT'
const apiUrl = 'http://localhost:8000'
const authProvider = {
login: ({username, password}) => {
const oAuthParams = {
username,
password
}
const body = Object.keys(oAuthParams).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(oAuthParams[key]);
}).join('&');
const request = new Request(`${apiUrl}/auth/jwt/login`, {
method: 'POST',
body: body,
headers: new Headers({'Content-Type': 'application/x-www-form-urlencoded'}),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(( {access_token} ) => {
inMemoryJWTManager.setToken(access_token);
});
},
checkError: (error) => {
const status = error.status;
if (status === 401 || status === 403) {
inMemoryJWTManager.ereaseToken();
return Promise.reject({redirectTo: '/login'});
}
// other error code (404, 500, etc): no need to log out
return Promise.resolve();
},
checkAuth: () => inMemoryJWTManager.getToken()
? Promise.resolve()
: Promise.reject({ message: 'Login necessário', redirectTo: 'login' }),
logout: () => {
inMemoryJWTManager.ereaseToken();
return Promise.resolve();
},
getPermissions: () => {
return inMemoryJWTManager.getToken() ? Promise.resolve() : Promise.reject();
},
};
export default authProvider;
我使用 inMemoryJWTManager 更新的 httpClient 代码:(我正在使用:const dataProvider = jsonServerProvider(apiUrl, httpClient); 并对其进行了修改,但我认为这无关紧要)
const httpClient = (url) => {
const options = {
headers: new Headers({ Accept: 'application/json' }),
};
const token = inMemoryJWTManager.getToken();
console.log(token)
if (token) {
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
} else {
inMemoryJWTManager.setRefreshTokenEndpoint(`${apiUrl}/auth/jwt/refresh`);
return inMemoryJWTManager.getRefreshedToken().then((gotFreshToken) => {
if (gotFreshToken) {
options.headers.set('Authorization', `Bearer ${inMemoryJWTManager.getToken()}`);
};
return fetchUtils.fetchJson(url, options);
});
}
};
我的问题是,当我调用我的刷新令牌端点时,我的请求没有 {'Authorization': Bearer... 并且它没有被更新并且我被注销了。其他端点都很好,它们与令牌一起使用。
【问题讨论】:
-
这是水疗中心吗?你用的是什么流量?
-
“我想做的就是在每次调用我的 api 时获取并更新我的令牌。”事情不是这样运作的。访问令牌是 JWT(JSON Web 令牌),具有一定的生命周期。您可以在jwt.io 查看您的访问令牌。要更新您的访问令牌,您有几个选项:auth0.com/docs/login/configure-silent-authentication 用于 spa,然后auth0.com/docs/security/tokens/refresh-tokens 用于混合流。
-
啊,没关系,看来您正在使用带有刷新令牌的混合流...
-
是的,它是一个单一的 SPA(带有 React-Admin)。我没有使用像 auth0 这样的身份验证提供程序
-
您使用什么作为您的身份验证提供程序?而且,对于 SPA,您应该使用带有 pkse 保护流的隐含或身份验证代码。而且,为此您需要进行静默更新。
标签: reactjs jwt react-admin