reactjs 获取过期后具有刷新标记的访问标记(JWT)

kognpnkq  于 2023-02-15  发布在  React
关注(0)|答案(2)|浏览(115)

过了一会儿,当页面被重新加载时,我收到401错误,我想这可能是因为访问令牌过期了。我如何用我的刷新令牌设置一个新的令牌?下面的函数在每次用户访问一个新页面或刷新页面时运行。但它似乎不起作用。

export async function currentAccount() {

  if (store.get('refreshToken')) {
    const query = {
      grant_type: 'refresh_token',
      companyId: store.get('lastCompanyId'),
      refresh_token: store.get('refreshToken'),
    }
    const queryString = new URLSearchParams(query).toString()
    const actionUrl = `${REACT_APP_SERVER_URL}/login?${queryString}`
    return apiClient
      .post(actionUrl, { auth: 'basic' })
      .then(async response => {
        if (response) {
          const { access_token: accessToken } = response.data
            store.set('accessToken', accessToken)
          return response.data
        }
        return false
      })
      .catch(err => {
        console.log('error', err)
        store.clearAll()
      })
  }
  return false
}

登录设置访问令牌

export async function login(email, password) {
  const query = {
    grant_type: 'password',
    username: email,
    password,
  }
  const queryString = new URLSearchParams(query).toString()
  const actionUrl = `${REACT_APP_SERVER_URL}/login?${queryString}`
  return apiClient
    .post(actionUrl, { auth: 'basic' })
    .then(async response => {
      if (response) {
        const {
          data: {
            access_token: accessToken,
            refresh_token: refreshToken,
          },
        } = response
        const decoded = jsonwebtoken.decode(accessToken)
        response.data.authUser = decoded.authUser
        const { userId, profileId, companyId } = decoded.authUser
        if (accessToken) {
          store.set('accessToken', accessToken)
          store.set('refreshToken', refreshToken)
        }
        return response.data
      }
      return false
    })
    .catch(err => console.log(err))
}

** Saga 用户.js**

export function* LOAD_CURRENT_ACCOUNT() {
  yield put({
    type: 'user/SET_STATE',
    payload: {
      loading: true,
    },
  })
  const { authProvider } = yield select((state) => state.settings)
  const response = yield call(mapAuthProviders[authProvider].currentAccount)
  if (response) {
    const decoded = jsonwebtoken.decode(response.access_token)
    response.authUser = decoded.authUser
    yield store.set('id', id)
    try {
      const user = yield call(LoadUserProfile)
      if (user) {
        const { company } = user
        yield put({
          type: 'user/SET_STATE',
          payload: {
            ...user,
            preferredDateFormat: user.preferredDateFormat || 'DD/MM/YYYY',
            userId,
            id,
          },
        })
      }
    } catch (error) {
    }
  }else{
    store.set('refreshToken', response.refreshToken)
  }

  yield put({
    type: 'user/SET_STATE',
    payload: {
      loading: false,
    },
  })
}
66bbxpm5

66bbxpm51#

你可以使用拦截器用你的刷新令牌获取一个新的访问令牌,拦截并检查响应状态码401,用你的刷新令牌获取一个新的访问令牌,并将新的访问令牌添加到头中。
示例:

return apiClient
  .post(actionUrl, { auth: 'basic' })
  .then(async response => {
    if (response) { // check for the status code 401 and make call with refresh token to get new access token and set in the auth header
      const { access_token: accessToken } = response.data
        store.set('accessToken', accessToken)
      return response.data
    }
    return false
  });

简单的拦截器例子

axios.interceptors.request.use(req => {
  req.headers.authorization = 'token';
  return req;
});

401拦截器示例

axios.interceptors.response.use(response => response, error => {

    if (error.response.status === 401) {
       // Fetch new access token with your refresh token
       // set the auth header with the new access token fetched
    }
 });

有几篇关于使用Interceptors通过刷新令牌获取新的访问令牌的好文章
https://thedutchlab.com/blog/using-axios-interceptors-for-refreshing-your-api-token
https://medium.com/swlh/handling-access-and-refresh-tokens-using-axios-interceptors-3970b601a5da
Automating access token refreshing via interceptors in axios
https://stackoverflow.com/a/52737325/8370370

ny6fqffe

ny6fqffe2#

上面的答案是好的。但是我发现下面的方法比使用Axios拦截器和"jwt-decode"更好。试试吧。(我在这个例子中使用了会话存储。你可以用你自己的方法安全地存储令牌)

    • 方法**

1.登录以获取访问令牌和长期刷新令牌,然后安全地存储它们。
1.创建一个axios示例,用"jwt-decode"检查访问令牌的过期时间,如果存在有效的访问令牌,则将访问令牌添加到请求中,否则使用存储的刷新令牌请求新的访问令牌,然后将新的访问令牌应用到请求中。
登录:

import axios from 'axios'

const handleLogin = async (login) => {
  await axios
  .post('/api/login', login, {
    headers: {
      'Content-Type': 'application/json'
    }
  })
  .then(async response => {
    sessionStorage.setItem('accessToken', response.data.accessToken)
    sessionStorage.setItem('refreshToken', response.data.refreshToken)
  })
  .catch(err => {
    if (errorCallback) errorCallback(err)
  })
 }

创建axios示例:

import axios from 'axios'
import jwt_decode from 'jwt-decode'
import dayjs from 'dayjs'

const axiosInstance = axios.create({
  headers: { 'Content-Type': 'application/json' }
})

axiosInstance.interceptors.request.use(async req => {
  const accessToken = sessionStorage.getItem('accessToken') ? sessionStorage.getItem('accessToken') : null

  if (accessToken) {
    req.headers.Authorization = `Bearer ${accessToken}`
  }

  const tokenData = jwt_decode(accessToken)
  const isExpired = dayjs.unix(tokenData.exp).diff(dayjs()) < 1

  if (!isExpired) return req

  const refreshToken = sessionStorage.getItem('refreshToken')

  const response = await axios.post('/api/refresh', { refreshToken }, {
    headers: {
      'Content-Type': 'application/json'
    }
  })

  req.headers.Authorization = `Bearer ${response.data.accessToken}`
  sessionStorage.setItem('accessToken', response.data.accessToken)

  return req
})

export default axiosInstance

在所有请求中使用axios示例(Redux Toolkit示例):

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'

// Import axiosInstance
import axiosInstance from 'src/utils/axiosInstance'

export const getItems = createAsyncThunk(
  'appItems/getItems',
  async (args, { rejectedWithValue }) => {
    try {
      
      const response = await axiosInstance.get('/api/items')

      return response.data
    } catch ({ response }) {
      return rejectedWithValue({ code: response.status, ...response.data })
    }
  }
)

相关问题