source

Redux를 사용하여 JWT 토큰을 새로 고치는 방법

ittop 2023. 2. 26. 10:32
반응형

Redux를 사용하여 JWT 토큰을 새로 고치는 방법

React Native Redux 앱은 인증에 JWT 토큰을 사용합니다.이러한 토큰을 필요로 하는 동작은 여러 가지가 있으며, 앱 로드 시 등에 많은 토큰이 동시에 디스패치됩니다.

예.

componentDidMount() {
    dispath(loadProfile());
    dispatch(loadAssets());
    ...
}

다.loadProfile ★★★★★★★★★★★★★★★★★」loadAssetsJWT에 대해서되며, 「 」는 「 」로 됩니다.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를 이용하다는 먼저 .thunkthunk용됩니니다다

...
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 될 때 약속을 스테이트에 .이 함수는 토큰이 갱신될 때 상태를 새로운 토큰에 대한 약속을 포함하도록 액션을 디스패치해야 합니다.이와 같이 토큰 인증을 동시에 사용하는 여러 비동기 액션을 디스패치하면 토큰은 한 번만 새로 고쳐집니다.

export function refreshToken(dispatch) {

    var freshTokenPromise = fetchJWTToken()
        .then(t => {
            dispatch({
                type: DONE_REFRESHING_TOKEN
            });

            dispatch(saveAppToken(t.token));

            return t.token ? Promise.resolve(t.token) : Promise.reject({
                message: 'could not refresh token'
            });
        })
        .catch(e => {

            console.log('error refreshing token', e);

            dispatch({
                type: DONE_REFRESHING_TOKEN
            });
            return Promise.reject(e);
        });



    dispatch({
        type: REFRESHING_TOKEN,

        // we want to keep track of token promise in the state so that we don't try to refresh
        // the token again while refreshing is in process
        freshTokenPromise
    });

    return freshTokenPromise;
}

난 이게 꽤 복잡하다는 걸 알아.또, 에서의 액션의 디스패치가 조금 걱정됩니다.refreshToken행동 자체가 아닙니다.유효기간이 지난 JWT 토큰을 redux로 처리하는 다른 방법을 알려주세요.

작업이 완료될 때까지 "대기"하는 대신 저장 변수를 유지하여 토큰을 계속 가져오는지 여부를 확인할 수 있습니다.

샘플 리듀서

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());
       }
    };
}

이것은 컴포넌트가 마운트되면 호출됩니다., 이는 " "를 합니다.fetching및를 새로 .프로파일 또는 자산은 아직 로드하지 않습니다.

새 구성 요소:

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()
    } 
}

이제 소품을 받을 때 특정 조건 하에서 물건을 적재하려고 시도합니다(매장이 바뀌면 호출됩니다).fetching여기서) 초기 페치가 실패했을 경우, 이 에러는,refreshToken이 작업이 완료되면 스토어에 새로운 토큰이 설정되고 컴포넌트가 갱신되어 호출됩니다.componentWillReceiveProps. 아직 가져오지 않은 경우(이 체크가 필요한지 확실하지 않음), 로딩됩니다.

나는 간단한 포장지를 만들었다.redux-api-middleware액션을 연기하고 액세스 토큰을 새로 고칩니다.

미들웨어.다운로드

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)}`
  })
}

그렇게redux-api-middleware동작은 거의 변경되지 않습니다.

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 리프레시 토큰 워크플로우가 동작하고 있는 것을 나타내는 프로젝트 예제를 작성했습니다.

redux는 토큰 리프레시의 원자성을 강제하기 위한 적절한 툴이 아니라고 생각합니다.

대신 어디서든 호출할 수 있고 항상 유효한 토큰을 얻을 수 있는 원자 함수를 제공할 수 있습니다.

/*
    The non-atomic refresh function
*/

const refreshToken = async () => {
    // Do whatever you need to do here ...
}

/*
    Promise locking-queueing structure
*/

var promiesCallbacks = [];

const resolveQueue = value => {
  promiesCallbacks.forEach(x => x.resolve(value));
  promiesCallbacks = [];
};
const rejectQueue = value => {
  promiesCallbacks.forEach(x => x.reject(value));
  promiesCallbacks = [];
};
const enqueuePromise = () => {
  return new Promise((resolve, reject) => {
    promiesCallbacks.push({resolve, reject});
  });
};

/*
    The atomic function!
*/

var actionInProgress = false;

const refreshTokenAtomically = () => {
  if (actionInProgress) {
    return enqueuePromise();
  }

  actionInProgress = true;

  return refreshToken()
    .then(({ access }) => {
      resolveQueue(access);
      return access;
    })
    .catch((error) => {
      rejectQueue(error);
      throw error;
    })
    .finally(() => {
      actionInProgress = false;
    });
};

여기에도 게재 : https://stackoverflow.com/a/68154638/683763

언급URL : https://stackoverflow.com/questions/36948557/how-to-use-redux-to-refresh-jwt-token

반응형