react-boilerplate身份验证登录页面在页面重新加载时闪烁

dor*_*ork 6 authentication reactjs redux-saga react-boilerplate

我正在开发一个带有登录页面和应用程序其余页面的应用程序(应登录查看).我正在使用 react-boilerplate.从这里example,我编辑了我的asyncInjectors.js文件redirectToLoginredirectToDashboard方法:

//asyncInjectors.js
export function redirectToLogin(store) {
  return (nextState, replaceState) => {
    const isAuthenticated = store.getState().get('app').get('isAuthenticated');

    if (!isAuthenticated) {
      replaceState({
        pathname: '/login',
        state: {
          nextPathname: nextState.location.pathname,
        },
      });
    }
  };
}

export function redirectToDashboard(store) {
  return (nextState, replaceState) => {
    const isAuthenticated = store.getState().get('app').get('isAuthenticated');

    if (isAuthenticated) {
      replaceState('/');
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,我只是设置redirectToLoginonEnter页面和redirectToDashboard登录页面.

它工作正常但是当F5登录时页面刷新()时,登录页面会短暂呈现,然后呈现实际页面.登录页面只是调度一个authenticate动作componentWillMount,然后重定向componentDidUpdate:

//login.js
componentWillMount() {
  this.props.dispatch(authenticate());
}

componentDidUpdate(prevProps, prevState) {
  if (this.props.isAuthenticated) {
    const nextPathname = prevProps.location.state ? prevProps.location.state.nextPathname : '/';

    browserHistory.push(nextPathname);
  }
}
Run Code Online (Sandbox Code Playgroud)

页面的容器也具有相同的componentWillMount代码.不确定是不是因为传奇,但这里是代码:

//sagas.js
export function* login({ user, password }) {
    try {
        const token = yield call(app.authenticate, {
            strategy: 'local',
            user,
            password,
        });

        return token;
    } catch (error) {
        return onError(error);
    }
}

// For page refresh after logging in
export function* authenticate() {
    try {
        const token = yield call(app.authenticate);

        return token;
    } catch (error) {
        return onError(error);
    }
}

export function* logout() {
    try {
        const response = yield call(app.logout);

        return response;
    } catch (error) {
        return onError(error);
    }
}

export function* loginFlow() {
    while (true) {
        const request = yield take(LOGIN_REQUEST);
        const winner = yield race({
            auth: call(login, request.data),
            logout: take(LOGOUT_REQUEST),
        });

        if (winner.auth && winner.auth.accessToken) {
            yield put(actions.setAuthState(true));
        }
    }
}

export function* logoutFlow() {
    while (true) {
        yield take(LOGOUT_REQUEST);
        yield put(actions.setAuthState(false));
        yield call(logout);
        browserHistory.push('/login');
    }
}

export function* authenticateFlow() {
    while (true) {
        yield take(AUTHENTICATE);

        const response = yield call(authenticate);

        if (response && response.accessToken) {
            yield put(actions.setAuthState(true));
        }
    }
}

export default [
    loginFlow,
    logoutFlow,
    authenticateFlow,
];
Run Code Online (Sandbox Code Playgroud)

如何摆脱闪烁的登录页面?

编辑:当我尝试gouroujo的答案时,我无法注销.

//asyncInjectors.js
import jwtDecode from 'jwt-decode';

export function redirectToLogin(store) {
    return (nextState, replaceState, callback) => {
        const token = localStorage.token;

        if (token) {
            const jwt = jwtDecode(token);

            if (jwt.exp <= (new Date().getTime() / 1000)) {
                store.dispatch(actions.setAuthState(false));

                replaceState({
                    pathname: '/login',
                    state: {
                        nextPathname: nextState.location.pathname,
                    },
                });
            }
        }

        store.dispatch(actions.setAuthState(true));
        callback();
    };
}
Run Code Online (Sandbox Code Playgroud)

当我点击刷新时,login page没有显示,但现在我无法注销.

gou*_*ujo 2

您有两种方法可以避免在初始渲染时闪烁登录页面:使您的身份验证功能同步或等待加载页面的答案。

1- 检查您的令牌是否存在且客户端有效(到期日期),以选择是否必须首先将用户重定向到登录或仪表板页面。当服务器返回答案时,您可以纠正最初的猜测,但绝大多数情况下您不需要这样做。

用户登陆->

  • 检查令牌客户端 -> 如果需要重定向到登录
  • 检查令牌服务器端 -> 等待答案 -> 如果需要,重新重定向

要检查令牌客户端,您必须检查本地存储。例如:

class App extends Component {
  requireAuth = (nextState, replace) => {
    if (!localStorage.token) {
      replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
      })
    }
  }
  render() {
    return (
      <Router history={browserHistory}>
        <Route path="/login" component={LoginPage} />
        <Route
          path="/"
          component={AppLayout}
          onEnter={this.requireAuth}
        > ... </Route>
     )
   }
 }
Run Code Online (Sandbox Code Playgroud)

如果您使用过期日期相对较短的令牌,您还必须检查过期日期,从而对令牌进行解码。

try {
      const jwt = JWTdecode(token);
      if (moment().isBefore(moment.unix(jwt.exp))) {
        return nextState;
      } else {
       replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
       })
      }
    } catch (e) {
      replace({
        pathname: '/login',
        state: { nextPathname: nextState.location.pathname }
      })
    }
Run Code Online (Sandbox Code Playgroud)

2- 在收到服务器的答复之前显示加载屏幕。添加一些不透明度的 CSS 过渡效果以避免“闪光”