当用户通过身份验证后,我可以刷新页面并通过网站或输入/刷新 URL 访问所有路由。然而,当用户未经身份验证时,虽然通过网站的路由运行良好,但即使在普通路由页面(非私有)上刷新 URL 也会将我重定向到索引(主页)。
我已使用 this 作为我的 React 应用程序的模板,并使用 this 为我的 React 应用程序添加身份验证。我分别尝试了这两个指南,并且都运行良好,但不知何故在我的 React 应用程序中,它们的组合导致了我上面描述的问题。
以下是我的应用路由代码:
import React, { Component, lazy, Suspense} from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import Spinner from '../app/shared/Spinner';
const MainIndex = lazy(() => import( "./mainpage/Index"));
const TermsIndex = lazy(() => import( "./mainpage/Terms"));
const Dashboardmain = lazy(() => import( "./dashboard/Dashboardmain"));
const Login = lazy(() => import( "./user-pages/Login"));
const Register = lazy(() => import( "./user-pages/Register"));
class AppRoutes extends Component {
render () {
return (
<Suspense fallback={<Spinner/>}>
<Switch>
<Route exact path="/login" component={ Login } />
<Route exact path="/index" component={ MainIndex } />
<Route exact path="/terms" component={ TermsIndex } />
<PrivateRoute exact path="/internal/dashboard" component={ Dashboardmain } />
<Route exact path="/register" component={ Register } />
</Switch>
</Suspense>
);
}
}
export default AppRoutes;
我的应用页面如下所示 - FullPageLayout 用于检查是否应为页面添加页眉、页脚或侧边栏:
import React, { Component } from 'react';
import AppRoutes from './AppRoutes';
import Navbar from './shared/Navbar';
import Sidebar from './shared/Sidebar';
import Footer from './shared/Footer';
import { withRouter } from 'react-router-dom';
class AppPages extends Component {
state = {}
componentDidMount() {
this.onRouteChanged();
}
render () {
let navbarComponent = !this.state.isFullPageLayout ? <Navbar/> : '';
let sidebarComponent = !this.state.isFullPageLayout ? <Sidebar/> : '';
let footerComponent = !this.state.isFullPageLayout ? <Footer/> : '';
return (
<div className="container-scroller">
{ sidebarComponent }
<div className="container-fluid page-body-wrapper">
{ navbarComponent }
<div className="main-panel">
<div className="content-wrapper">
<AppRoutes/>
</div>
{ footerComponent }
</div>
</div>
</div>
);
}
componentDidUpdate(prevProps) {
if (this.props.location.pathname !== prevProps.location.pathname) {
this.onRouteChanged();
}
}
onRouteChanged() {
const containment = [];
const fullPageLayoutRoutes = ['/login', '/register',
'/index', '/terms'];
const body = document.querySelector('body');
body.classList.remove('rtl')
for (var i = 0; i < fullPageLayoutRoutes.length; i++) {
containment[containment.length] = this.props.location.pathname.toLowerCase().includes(fullPageLayoutRoutes[i])
}
if (containment.includes(true)) {
this.setState({isFullPageLayout: true})
document.querySelector('.page-body-wrapper').classList.add('full-page-wrapper');
} else {
this.setState({isFullPageLayout: false})
document.querySelector('.page-body-wrapper').classList.remove('full-page-wrapper');
}
}
}
export default withRouter(AppPages);
我的应用程序如下:
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom';
import './App.scss';
import { Provider } from "react-redux";
import jwt_decode from "jwt-decode";
import setAuthToken from "../utils/setAuthToken";
import { setCurrentUser, logoutUser } from "../actions/authActions";
import store from "../store";
import AppPages from './AppPages';
// Check for token to keep user logged in
if (localStorage.jwtToken) {
// Set auth token header auth
const token = localStorage.jwtToken;
setAuthToken(token);
// Decode token and get user info and exp
const decoded = jwt_decode(token);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000; // to get in milliseconds
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Redirect to login
window.location.href = "/login";
}
}
class App extends Component {
render () {
return (
<Provider store={store}>
<BrowserRouter>
<AppPages/>
</BrowserRouter>
</Provider>
);
}
}
export default App;
我尝试通过更改/简化路由器或应用程序部分中的内容来调试问题,但我认为问题不在这里。我认为问题出在减速器或操作中。
我的店铺定义如下:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__()) ||
compose
)
);
export default store;
我的 authReducer 定义为:
import { SET_CURRENT_USER, USER_LOADING } from "../actions/types";
const isEmpty = require("is-empty");
const initialState = {
isAuthenticated: false,
user: {},
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case USER_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
我的 authAction 定义为:
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from "./types";
// Register User
export const registerUser = (userData, history) => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => history.push("/login")) // re-direct to login on successful register
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Login - get user token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
// User loading
export const setUserLoading = () => {
return {
type: USER_LOADING
};
};
// Log user out
export const logoutUser = () => dispatch => {
// Remove token from local storage
localStorage.removeItem("jwtToken");
// Remove auth header for future requests
setAuthToken(false);
// Set current user to empty object {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
答案 0 :(得分:1)
这与执行顺序有关。刷新时 redux 状态会被擦除,因此您需要在 store.dispatch(setCurrentUser(decoded))
尝试呈现之前调用 PrivateRoute
。我不清楚哪里出错了,因为 if (localStorage.jwtToken) {
块不是 async
,尽管 dispatch
可能是?
我建议将 initialState
中的 authReducer
设置为 isAuthenticated: null
,并在您检查令牌后更新为 true
或 false
。现在您的 PrivateRoute
只知道两种状态:已验证和未验证。我们需要它来理解第三个“我还不知道”。在您的 PrivateRoute
中,当 isAutheticated
为 null
时,您将不渲染任何内容或加载微调器。在确定 Redirect
之前不要渲染 false
。
答案 1 :(得分:1)
所以经过一些调试后,我意识到我的问题来自 AppPages
和带有 { navbarComponent }
的那一行。此导航栏与 PrivateRoute
一起使用,需要定义用户。当用户未经身份验证时,虽然这在技术上不会为普通路由呈现,但它仍在处理,并且在此 navbar
中是一个条件,如果用户未定义,将返回到 index
页面。< /p>