【问题标题】:How to ensure JWT validation occurs before React/Redux router redirect?如何确保在 React/Redux 路由器重定向之前发生 JWT 验证?
【发布时间】:2019-04-19 01:52:11
【问题描述】:

我正在使用 React/Redux、Knex + Objection.Js + PostgreSQL 为 DB 和 feathersjs 为 API 框架开发一个全栈 PRN 应用程序。因此,我也在前端使用@feathersjs/client 及其身份验证包。我也在使用connected-react-router 进行路由。不幸的是,每当我尝试导航到受保护的路由时,负责设置用户状态的“登录”请求(从他们的 jwt 与服务器进行身份验证)在重定向将用户带到登录页面之前不会完成。

我正在通过调度一个操作来验证 react 应用程序的 index.js 文件中的 jwt。

if (localStorage['feathers-jwt']) {
  try {
       store.dispatch(authActions.login({strategy: 'jwt', accessToken: localStorage.getItem('feathers-jwt')}));
  }
  catch (err){
      console.log('authenticate catch', err);
  }
}

该动作由redux-saga 拾取,它执行以下动作

export function* authSubmit(action) {
  console.log('received authSubmit');
  try {
    const data = yield call(loginApi, action);
    yield put({type: authTypes.LOGIN_SUCCESS, data});

  } catch (error) {
      console.log(error);
      yield put({type: authTypes.LOGIN_FAILURE, error})
  }
}

function loginApi(authParams) {
  return services.default.authenticate(authParams.payload)
}

这是我的 isAuthenticated 函数和配置对象:

const isAuthenticated =  connectedReduxRedirect({
  redirectPath: '/login',
  authenticatedSelector: state => state.auth.user !== null,
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsAuthenticated'
});

这是应用于容器组件的 HOC

const Login = LoginContainer;
const Counter = isAuthenticated(CounterContainer);
const LoginSuccess = isAuthenticated(LoginSuccessContainer);

最后,这是渲染

export default function (store, history) {
  ReactDOM.render(
    <Provider store={store}>
      <ConnectedRouter history={history}>
        <Switch>
          <Route exact={true} path="/" component={App}/>
          <Route path="/login" component={Login}/>
          <Route path="/counter" component={Counter}/>
          <Route path="/login-success" component={LoginSuccess}/>
          <Route component={NotFound} />
        </Switch>
      </ConnectedRouter>
    </Provider>,
    document.getElementById('root')
  );
}

登录并访问时,例如/counter,我期望发生的情况如下

  1. LOGIN_REQUEST 操作已触发

  2. LOGIN_SUCCESS 操作已触发,用户已通过 JWT 身份验证

  3. 路由器看到 user.auth 对象不为空,因此用户是 已认证

  4. 路由器允许无重定向导航

我看到的是以下内容(手动导航到 /counter 时)

  1. @@INIT

  2. auth/LOGIN_REQUEST [这很好,loggingIn: true]

  3. @@router/LOCATION_CHANGE

{
  type: '@@router/LOCATION_CHANGE',
  payload: {
    location: {
      pathname: '/counter',
      search: '',
      hash: ''
    },
    action: 'POP',
    isFirstRendering: true
  }
}
  1. @@router_LOCATION_CHANGE [这是问题]
  type: '@@router/LOCATION_CHANGE',
  payload: {
    location: {
      pathname: '/login',
      hash: '',
      search: '?redirect=%2Fcounter',
      key: 'kdnf4l'
    },
    action: 'REPLACE',
    isFirstRendering: false
  }
}
  1. 用户导航到/login,这会按照当前设计将用户注销。

  2. LOGOUT_REQUEST -> LOGIN_SUCCESS -> LOCATION_CHANGE(到/login-success)

再次,我们将不胜感激任何帮助,如果需要,我可以提供其他任何东西。

谢谢!

-布伦登

【问题讨论】:

    标签: reactjs redux jwt redux-saga feathersjs


    【解决方案1】:

    解决方案

    我今天通过查看身份验证包feathers-reduxify-authentication 的功能解决了这个问题。在大多数情况下,重定向配置正确。

    后端

    authentication.js

    注意多种策略,以及如何返回 context.result。这是feathers-reduxify-authentication 正常工作所必需的。

    module.exports = function (app) {
      const config = app.get('authentication');
    
      // Set up authentication with the secret
      app.configure(authentication(config));
      app.configure(jwt());
      app.configure(local(config.local));
    
    
      app.service('authentication').hooks({
        before: {
          create: [
            authentication.hooks.authenticate(config.strategies),
          ],
          remove: [
            authentication.hooks.authenticate('jwt')
          ]
        },
        after: {
          create: [
            context => {
              context.result.data = context.params.user;
              context.result.token = context.data.accessToken;
              delete context.result.data.password;
              return context;
            }
          ]
        }
      });
    };
    

    前端

    src/feathers/index.js

    这是根据eddystop的示例项目,但升级到feathers 3.0+

    import feathers from '@feathersjs/client';
    import  io  from 'socket.io-client';
    import reduxifyAuthentication from 'feathers-reduxify-authentication';
    import reduxifyServices, { getServicesStatus } from 'feathers-redux';
    import { mapServicePathsToNames, prioritizedListServices } from './feathersServices';
    const hooks = require('@feathersjs/client');
    
    const socket = io('http://localhost:3030');
    const app = feathers()
      .configure(feathers.socketio(socket))
      .configure(hooks)
      .configure(feathers.authentication({
        storage: window.localStorage
      }));
    export default app;
    
    // Reduxify feathers-client.authentication
    export const feathersAuthentication = reduxifyAuthentication(app,
      { authSelector: (state) => state.auth.user}
    );
    // Reduxify feathers services
    export const feathersServices = reduxifyServices(app, mapServicePathsToNames);
    export const getFeathersStatus =
      (servicesRootState, names = prioritizedListServices) =>
        getServicesStatus(servicesRootState, names);
    

    中间件和存储。 src/state/configureStore

    redux-saga 暂时被移除了,等我测试完我会把它带回来

    import { createBrowserHistory } from 'history';
    import { createStore, applyMiddleware, compose } from "redux";
    import { routerMiddleware  } from 'connected-react-router';
    import createRootReducer from './ducks';
    import promise  from 'redux-promise-middleware';
    import reduxMulti from 'redux-multi';
    import rootSaga from '../sagas';
    import createSagaMiddleware from 'redux-saga';
    export default function configureStore(initialState) {
    
        const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
            || compose;
    
        const middlewares = [
            //sagaMiddleware,
            promise,
            reduxMulti,
            routerMiddleware(history)];
    
        const store = createStore(
            createRootReducer(history),
            initialState,
            composeEnhancer(
                applyMiddleware(
                    ...middlewares
                )
            )
        );
    
        return store;
    }
    

    根减速器,src/state/ducks/index.js

    import { combineReducers } from "redux";
    import { connectRouter } from 'connected-react-router';
    import { reducer as reduxFormReducer } from 'redux-form';
    import {feathersAuthentication, feathersServices} from '../../feathers';
    import counter from './counter';
    
    const rootReducer = (history) => combineReducers({
        counter,
        router: connectRouter(history),
        users: feathersServices.users.reducer,
        auth: feathersAuthentication.reducer,
        form: reduxFormReducer, // reducers required by redux-form
    
    });
    
    export default rootReducer;
    

    【讨论】:

      猜你喜欢
      • 2019-03-12
      • 2016-09-01
      • 2019-09-28
      • 2020-06-11
      • 2018-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多