受保护的路由 React Router 4 无法使用存储在 Redux 中的身份验证状态

Posted

技术标签:

【中文标题】受保护的路由 React Router 4 无法使用存储在 Redux 中的身份验证状态【英文标题】:Protected Routes React Router 4 not working with auth state stored in Redux 【发布时间】:2018-09-21 11:54:32 【问题描述】:

我正在尝试按照example 在 React Router v4 中创建经过身份验证的路由。显示后代的代码:

function PrivateRoute (component: Component, authed, ...rest) 
  return (
    <Route
      ...rest
      render=(props) => (!!authed)
        ? <Component ...props />
        : <Redirect to=pathname: '/login', state: from: props.location />
    />
  )

我的身份验证状态 (authed) 在 reducer 中初始化为空对象,它是从 Redux 存储中派生的。这就是我的 App.js 的样子:

class App extends Component 
  componentDidMount() 
    const token = localStorage.getItem("token");
    if (token) 
      this.props.fetchUser();
    
  

  render() 
    return (
      <Router>
        <div>
          <PrivateRoute authed=this.props.authed path='/dashboard' component=Dashboard />
          />
        </div>
      </Router>
    );
  

问题在于authed 状态以未定义的形式开始,然后,一旦安装了路由器组件,它就会将状态更新为true。然而这有点晚了,因为用户已经被重定向回登录页面。我还尝试用componentWillMount() 替换componentDidMount() 生命周期方法,但这也没有解决问题。

你会建议什么策略?

更新 1:我解决此问题的唯一方法是在返回 &lt;Route /&gt; 组件之前测试 authed 状态,例如:

  render() 
    if (!!this.props.authed) 
      return (
        <Router>
      <div>
      ...

更新 2:我正在使用 Redux Thunk 中间件来调度操作。状态作为道具正确传递 - 我在 PrivateRoute 组件中使用 console.log() 方法来验证状态是否正确变化。问题当然是变异晚了,Route 已经在重定向用户了。

reducer 和 action 的粘贴代码...

行动:

export const fetchUser = () => async dispatch => 
  dispatch( type: FETCHING_USER );
  try 
    const res = await axios.get(`$API_URL/api/current_user`, 
      headers:  authorization: localStorage.getItem("token") 
    );
    dispatch( type: FETCH_USER, payload: res.data );
   catch (err) 
    // dispatch error action types
  
;

减速机:

const initialState = 
  authed: ,
  isFetching: false
;
...
    case FETCH_USER: // user authenticated
      return  ...state, isFetching: false, authed: action.payload ;

【问题讨论】:

你是如何获得 'authed' 的值的,你有没有试过调查为​​什么 authed 调用需要很长时间,你能分享那个代码。 @alowsarwar 这是对 nodejs 服务器的 Axios 调用,返回 Passport 用户对象(从 jwt 令牌反序列化) @James 您能告诉我有关 UPDATE 1 解决方案的信息吗,您在哪里查看 (!!this.props.authed)?我也有同样的问题。 @Arnab 在渲染 &lt;Route /&gt; 组件之前,我正在测试 authed 状态是否存在。换句话说,为了回答你的问题,我正在我的路线文件中检查它。 @James 好的,谢谢。我将尝试您的解决方案,同时尝试用不同的方法解决它。这不是最优选的方法。但现在问题没有发生。你可以查看我的答案,我已经在下面发布了。 【参考方案1】:

我遇到了同样的问题,据我了解,您的更新 #1 应该是答案。然而,经过进一步调查,我认为这是一个架构问题。您的 Private 路由的当前实现取决于同步的信息。

如果我们务实地考虑它,ProtectedRoute 本质上会根据应用程序的状态返回重定向或组件。我们可以将所有路由包装在一个组件中并从存储中提取我们的信息,而不是用一个组件包装每个 Route

是的,每个受保护的路由需要编写更多代码,您需要测试这是否是一个可行的解决方案。

编辑:忘记提及这是架构问题的另一个重要原因是,如果用户刷新受保护的页面,他们将被重定向。

更新 更好的解决方案: 刷新时,如果他们通过身份验证,它将重定向到他们的目标 uri https://tylermcginnis.com/react-router-protected-routes-authentication/

解决方案 1

//You can make this a method instead, that way we don't need to pass authed as an argument
function Protected(authed, Component, props) 
  return !!authed
    ? <Component ...props />
    : <Redirect to='/login' />


class AppRouter extends React.PureComponent 
  componentDidMount() 
    const token = localStorage.getItem("token");
    if (token) 
      this.props.fetchUser();
    
  

  render() 
    let authed = this.props.authed

    return (
      <Router>
          <Route path='/protected' render=(props) => Protected(authed, Component, props) />
      </Router>
    )
  


class App extends Component 
  render() 
    return (
      <Provider store=store>
        <AppRouter />
      </Provider>
    )
  

解决方案 2 或者我们可以只检查每个组件(是的,这很痛苦)

class Component extends React.Component 
  render() 
    return (
      !!this.props.authed
        ? <div>...</div>
        : <Redirect to='/' />
    )
  

【讨论】:

【参考方案2】:

同样的问题发生在我身上,我正在使用临时破解方法来解决它,方法是在 localStorage 中存储一个加密值,然后在我的 PrivateRoute 组件中解密它并检查该值是否匹配。

action.js

localStorage.setItem('isAuthenticated', encryptedText);

PrivateRoute.js

if (localStorage.getItem('isAuthenticated')) 
const isAuth = decryptedText === my_value;
return (
     <Route
        ...rest
        render=(props) =>
           isAuth ? <Component ...props /> : <Redirect to="/login" />
        
     />
  );
 else 
      return <Redirect to="/login" />;
   

由于localStorage 更快,所以它没有不必要的重定向。如果有人删除localStorage,他们只会被重定向到/login 注意:这是一个临时解决方案。

【讨论】:

【参考方案3】:

理论上,您需要从 NODE API 调用中获得一个您现在没有得到的承诺。您需要进行架构更改。我建议你使用 redux-promise-middleware 这是一个 redux 中间件。我的github 帐户中有一个示例项目。如果您对 this.props.fetchUser() 的调用是否完成,您将在哪里收到通知,基于使用 Promise 您可以处理您遇到的这个异步问题。如果需要帮助,请去那个 repo 询问我。

【讨论】:

不确定我是否理解。我正在使用 Redux Thunk 来调度这些操作。【参考方案4】:

如果您的 App 组件连接到 redux 存储,您的意思可能是 this.props.authed 而不是 this.state.authed

我的认证状态(authed),初始化为空 reducer 中的对象,来自 Redux 存储

所以您在这里将空对象与true 进行比较:(props) =&gt; authed === true?为什么不用false 初始化它?

您确定this.props.fetchUser 操作正在将状态切换为true

也许你最好也发布你的 action 和 reducer 文件

【讨论】:

是的,我在粘贴其他 SO 答案的示例时修改了代码。

以上是关于受保护的路由 React Router 4 无法使用存储在 Redux 中的身份验证状态的主要内容,如果未能解决你的问题,请参考以下文章

React Router 受保护的路由

页面未呈现;在 react-router-dom 中使用受保护的路由将道具传递给孩子时

React-Router-Dom v6 保护路由

使用 React Router 时无法访问 App 组件

受保护的路由无法与 React 和 Firebase 一起正常工作

使用反应路由器 v6 的受保护路由