Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何使用Redux刷新JWT令牌?_Javascript_Reactjs_React Native_Redux_Jwt - Fatal编程技术网

Javascript 如何使用Redux刷新JWT令牌?

Javascript 如何使用Redux刷新JWT令牌?,javascript,reactjs,react-native,redux,jwt,Javascript,Reactjs,React Native,Redux,Jwt,我们的React Native Redux应用程序使用JWT令牌进行身份验证。有许多操作需要这样的令牌,并且很多令牌是同时调度的,例如当应用程序加载时 例如 loadProfile和loadAssets都需要JWT。我们将令牌保存在状态和AsyncStorage。我的问题是如何处理令牌过期 最初,我打算使用中间件来处理令牌过期 // jwt-middleware.js export function refreshJWTToken({ dispatch, getState }) { re

我们的React Native Redux应用程序使用JWT令牌进行身份验证。有许多操作需要这样的令牌,并且很多令牌是同时调度的,例如当应用程序加载时

例如

loadProfile
loadAssets
都需要JWT。我们将令牌保存在状态和
AsyncStorage
。我的问题是如何处理令牌过期

最初,我打算使用中间件来处理令牌过期

// jwt-middleware.js

export function refreshJWTToken({ dispatch, getState }) {

  return (next) => (action) => {
    if (isExpired(getState().auth.token)) {
      return dispatch(refreshToken())
          .then(() => next(action))
          .catch(e => console.log('error refreshing token', e));
    }
    return next(action);
};
}


我遇到的问题是,
loadProfile
loadAssets
操作都会刷新令牌,因为在它们分派时,令牌将过期。理想情况下,我希望在刷新令牌之前“暂停”需要身份验证的操作。有没有办法用中间件做到这一点?

我找到了一种方法来解决这个问题。我不确定这是否是最佳实践方法,可能还有一些改进

我最初的想法是:JWT刷新在中间件中。如果使用
thunk
,则该中间件必须位于
thunk
之前

...
const createStoreWithMiddleware = applyMiddleware(jwt, thunk)(createStore);
然后在中间件代码中,我们检查令牌是否在任何异步操作之前过期。如果过期,我们也会检查是否已经在刷新令牌——为了能够进行这样的检查,我们会向状态中添加对新令牌的承诺

import { refreshToken } from '../actions/auth';

export function jwt({ dispatch, getState }) {

    return (next) => (action) => {

        // only worry about expiring token for async actions
        if (typeof action === 'function') {

            if (getState().auth && getState().auth.token) {

                // decode jwt so that we know if and when it expires
                var tokenExpiration = jwtDecode(getState().auth.token).<your field for expiration>;

                if (tokenExpiration && (moment(tokenExpiration) - moment(Date.now()) < 5000)) {

                    // make sure we are not already refreshing the token
                    if (!getState().auth.freshTokenPromise) {
                        return refreshToken(dispatch).then(() => next(action));
                    } else {
                        return getState().auth.freshTokenPromise.then(() => next(action));
                    }
                }
            }
        }
        return next(action);
    };
}
我意识到这相当复杂。我还有点担心在
refreshToken
中调度操作,因为它本身不是一个操作。请告诉我您知道的任何其他使用redux处理过期JWT令牌的方法。

您可以保留一个存储变量,以了解是否仍在获取令牌,而不是“等待”操作完成:

样品还原剂

const initialState = {
    fetching: false,
};
export function reducer(state = initialState, action) {
    switch(action.type) {
        case 'LOAD_FETCHING':
            return {
                ...state,
                fetching: action.fetching,
            }
    }
}
现在,动作创建者:

export function loadThings() {
    return (dispatch, getState) => {
        const { auth, isLoading } = getState();

        if (!isExpired(auth.token)) {
            dispatch({ type: 'LOAD_FETCHING', fetching: false })
            dispatch(loadProfile());
            dispatch(loadAssets());
       } else {
            dispatch({ type: 'LOAD_FETCHING', fetching: true })
            dispatch(refreshToken());
       }
    };
}
当安装组件时,将调用该函数。如果身份验证密钥过时,它将调度一个操作,将
获取
设置为true,并刷新令牌。请注意,我们还没有加载配置文件或资产

新的组成部分:

componentDidMount() {
    dispath(loadThings());
    // ...
}

componentWillReceiveProps(newProps) {
    const { fetching, token } = newProps; // bound from store

    // assuming you have the current token stored somewhere
    if (token === storedToken) {
        return; // exit early
    }

    if (!fetching) {
        loadThings()
    } 
}

请注意,现在您尝试在挂载上加载您的物品,但在接收道具时也会在某些条件下加载(当存储更改时会调用此命令,以便我们可以在那里继续
获取
),当初始获取失败时,它将触发
刷新令牌
。完成后,它将在存储中设置新令牌,更新组件,并因此调用
componentWillReceiveProps
。如果它还没有抓取(不确定是否需要此检查),它将加载内容。

我在
redux api中间件
上做了一个简单的包装,以推迟操作并刷新访问令牌

中间件.js

import { isRSAA, apiMiddleware } from 'redux-api-middleware';

import { TOKEN_RECEIVED, refreshAccessToken } from './actions/auth'
import { refreshToken, isAccessTokenExpired } from './reducers'


export function createApiMiddleware() {
  const postponedRSAAs = []

  return ({ dispatch, getState }) => {
    const rsaaMiddleware = apiMiddleware({dispatch, getState})

    return (next) => (action) => {
      const nextCheckPostponed = (nextAction) => {
          // Run postponed actions after token refresh
          if (nextAction.type === TOKEN_RECEIVED) {
            next(nextAction);
            postponedRSAAs.forEach((postponed) => {
              rsaaMiddleware(next)(postponed)
            })
          } else {
            next(nextAction)
          }
      }

      if(isRSAA(action)) {
        const state = getState(),
              token = refreshToken(state)

        if(token && isAccessTokenExpired(state)) {
          postponedRSAAs.push(action)
          if(postponedRSAAs.length === 1) {
            return  rsaaMiddleware(nextCheckPostponed)(refreshAccessToken(token))
          } else {
            return
          }
        }
      
        return rsaaMiddleware(next)(action);
      }
      return next(action);
    }
  }
}

export default createApiMiddleware();
我将令牌保持在该状态,并使用一个简单的助手将访问令牌注入到请求头中

export function withAuth(headers={}) {
  return (state) => ({
    ...headers,
    'Authorization': `Bearer ${accessToken(state)}`
  })
}
因此,
reduxapi中间件
操作几乎保持不变

export const echo = (message) => ({
  [RSAA]: {
      endpoint: '/api/echo/',
      method: 'POST',
      body: JSON.stringify({message: message}),
      headers: withAuth({ 'Content-Type': 'application/json' }),
      types: [
        ECHO_REQUEST, ECHO_SUCCESS, ECHO_FAILURE
      ]
  }
})

我写了并分享了,这表明JWT刷新令牌工作流正在运行

谢谢!这对于初始负载来说绝对有意义。但我不确定它是否适用于在应用程序加载和使用后使代币过期。对API的每次调用都需要有效的令牌。我们有许多需要登录和加载数据的弹出视图,因此我不确定通过这些视图的道具处理过期是否有效。您可以更改逻辑以检查令牌的过期,而不是令牌的差异。我们的想法是,任何操作都会触发此生命周期方法,因此您可以使用它来更新
fetching
变量,并根据我的第一个问题作出反应,向每个需要JWT的操作添加
dispatch({type:'LOAD\u fetching',fetching:true})
。第二个问题是如何知道刷新何时完成。假设有一个“AddtoFavorites”按钮,用于调度需要auth的api调用。是否要将“如果令牌过期刷新,则进行调用”逻辑添加到该操作?其他类似的行动呢?这就是我尝试使用中间件的原因。在其他框架/语言中,我使用了decorator,但我不确定我是否可以用React实现。啊,是的,它会变得重复,肯定应该是中间件。装潢师是有道理的,但我不确定你是否能使用他们。另一种策略是通过中间件将您的操作(如
“ADD_to_FAVS”
)排入队列数组。立即尝试分派,但如果令牌已过时,请刷新它。同时,订阅此更改并在任何更改时尝试清空队列。发送会有延迟,但不会超过这种握手的预期。我建议您查看一个名为。。。它完美地解决了这个问题。@KevinHe:你能再分享一点redux saga是如何解决这个问题的吗?你可以让refreshToken接收一个“延迟动作”,如果刷新成功,它将被发送,而不是返回一个新的承诺。至少我是这样解决的。@Shvetusya我不会担心在refreshToken中调度操作,因为它本身不是一个操作。refreshToken本质上是一个action creator,像这样在actionCreator中调度其他操作是非常常见的做法。对于这段代码,有很多操作!也许在所有操作之后,我们需要从状态中删除freshTokenPromise对象?返回getState().auth.freshTokenPromise.then(()=>next(action)).then(()=>{dispatch({type:REFRESHING\u TOKEN\u PROMISE\u CLEAN,freshTokenPromise:null,})漂亮!需要注意的是,对于那些具有
redux persist
的用户,不支持持久承诺,
freshTokenPromise
必须使用transformer@Jawla下面是一个例子,希望能有所帮助
export function withAuth(headers={}) {
  return (state) => ({
    ...headers,
    'Authorization': `Bearer ${accessToken(state)}`
  })
}
export const echo = (message) => ({
  [RSAA]: {
      endpoint: '/api/echo/',
      method: 'POST',
      body: JSON.stringify({message: message}),
      headers: withAuth({ 'Content-Type': 'application/json' }),
      types: [
        ECHO_REQUEST, ECHO_SUCCESS, ECHO_FAILURE
      ]
  }
})