【问题标题】:React-Admin Simple Refresh JWT TokenReact-Admin 简单刷新 JWT 令牌
【发布时间】: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


【解决方案1】:

您必须在每个请求之前检查令牌过期,如果令牌过期您必须从/auth/jwt/refresh 获取新令牌,然后您可以发送当前请求。所有这些信息都在文章post 中。示例:

const httpClient = (url) => {
    const options = {
        headers: new Headers({ Accept: 'application/json' }),
    };
    const token = inMemoryJWT.getToken();

    if (token) {
        options.headers.set('Authorization', `Bearer ${token}`);
        return fetchUtils.fetchJson(url, options);
    } else {
        inMemoryJWT.setRefreshTokenEndpoint('http://localhost:8001/refresh-token');
        return inMemoryJWT.getRefreshedToken().then((gotFreshToken) => {
            if (gotFreshToken) {
                options.headers.set('Authorization', `Bearer ${inMemoryJWT.getToken()}`);
            };
            return fetchUtils.fetchJson(url, options);
        });
    }
};

【讨论】:

  • 我试过了,但我收到“令牌更新失败”,因为我当前的令牌不符合我的标头请求...我该如何发送它?
  • Token renewal failure 因为你错过了Authorization 标头,只需设置正确的标头即可刷新令牌请求{'Authorization': Bearer ${inMemoryJWT.getToken()})}
  • 我不知道该怎么做。对于其他端点,我的标头是正确的,但对于刷新端点,它没有 {'Authorization': Bearer... 我更新了上面的代码,包括我的 inMemoryJWTManagerauthProviderhttpClient 更新
  • 我更改了它,现在我将承载发送到刷新端点(将getRefreshedTokeninMemoryJW 更改。但我收到Status: 304 Not Modified。响应中的缓存是:@987654334 @. 我不明白这个缓存,我的令牌持续 24 小时。
  • 算了,反正按F5不发送Bearer,导航到其他资源也不发送。
猜你喜欢
  • 1970-01-01
  • 2019-03-03
  • 2016-03-05
  • 2016-06-25
  • 1970-01-01
  • 2021-04-06
  • 2017-10-11
  • 2016-10-14
  • 2020-06-09
相关资源
最近更新 更多