首页 > 解决方案 > Redux 和令牌认证:如何重定向用户

问题描述

我制作了自己的 Django rest Framework API,并包含了令牌身份验证。一切正常,我可以获得令牌和东西,但是,我在重定向我的用户时遇到了一些麻烦,使用:

this.props.push('/');

这是 UI 身份验证背后的逻辑:

import React, {Component} from 'react';
import { connect } from 'react-redux';
import {authLogin} from '../../actions/authentication.js';

class Login extends Component {

  constructor(props){
    super(props);
    this.state = {
      email: "",
      password: ""
    }
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e){
    this.setState({[e.target.name]: e.target.value});
  }

  handleSubmit(e){
    e.preventDefault();
    this.props.authLogin(this.state.email, this.state.password);
    console.log(this.props.isAuthenticated);
    console.log(this.props.loading);
      if (this.props.isAuthenticated){
        this.props.history.push('/');
    }
  }

  render(){
    return(
      <div className="Login">
        <h1> This is a form </h1>
        <form>
          <input type="text" onChange={this.handleChange} name="email"/>
          <input type="password" onChange={this.handleChange} name="password"/>
          <input type="submit" onClick={this.handleSubmit}/>
          { this.props.isAuthenticated &&
            <p>Hello </p>
          }
        </form>
      </div>
    )
  }
}

const mapStateToProps = state => {
  console.log(state);
  return {
    token: state.authentication.token
  }
}

export default connect(mapStateToProps, {authLogin})(Login);

此登录组件位于容器组件内,这是我希望 Redux 告诉“嘿,这是新令牌”的地方:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {connect } from 'react-redux';
import AppHeader from './header/AppHeader';
import HomePageStream from './HomePageStream/HomePageStream.js';
import HeaderCategories from './header/HeaderCategories.js';
import ArticleDetails from './ArticleDetails/ArticleDetails.js';
import { Route, Switch } from 'react-router-dom';
import { transitions, positions, Provider as AlertProvider } from 'react-alert'
import AlertTemplate from 'react-alert-template-basic'
import Alerts from './Alerts/Alerts.js';
import {authStateCheck} from '../actions/authentication.js';
import { Provider } from 'react-redux'
import { store } from '../store.js';
import { BrowserRouter } from "react-router-dom";
import Login from './Authentication/Login.js';


const options = {
  timeout: 2000,
  position: "top center"
}


class Container extends Component {

  componentDidMount(){
    this.props.authStateCheck();
  }

  render(){
    return(
      <div className="Container">
        <Provider store={store}>
            <AlertProvider template={AlertTemplate}
                           {...options}
            >
            <AppHeader />
            <Alerts />
            <HeaderCategories />
            <Switch>
                <Route exact
                       path="/:category"
                       render={routerProps => <HomePageStream {...routerProps}/>}
                />
                <Route exact
                       path="/article/:slug"
                       render={routerProps => <ArticleDetails {...routerProps}/>}
                />
                <Route exact
                       path="/authentication/login"
                       render={ routerProps => <Login {...routerProps}{...this.props}/>}
                />
            </Switch>
            </AlertProvider>
        </Provider >
      </div>
    )
  }
}

const mapStateToProps = state => {
  console.log(state.authentication);
  return {
    isAuthenticated: state.authentication.token !== null,
    loading: state.authentication.loading
  }
}

export default connect(mapStateToProps, {authStateCheck} )(Container);

这是问题:当我点击提交时,isAuthenticated 是假的,因为我只在调用 handleSubmit() 之后才获得令牌!

这是我的操作的代码:

import axios from 'axios';
import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from './type';


export const authStart = () => {
  return {
    type: AUTH_START,
    loading: true
  };
}

export const authSucess = token => {
  console.log(token);
  return {
    type: AUTH_SUCESS,
    token: token,
    error: null,
    loading: false
  };
}

export const authFail = error => {
  console.log(error)
  return {
    token: null,
    type: AUTH_FAIL,
    error: error,
    loading: false
  };
}


export const logout = () => {
  window.localStorage.removeItem('token');
  window.localStorage.removeItem('expiration_time');
  return {
    type: AUTH_LOGOUT
  }
}


export const checkAuthTimeOut = expiration_time => dispatch => {
  return setTimeout(()=> {
    dispatch(logout());
  }, expiration_time * 1000);
}



export const authLogin = (email, password) => dispatch => {
  dispatch(authStart());
  console.log("I'm authlogin ! ");
  axios.post('http://127.0.0.1:8000/rest-auth/login/',{
      "email": email,
      "password": password
  })
  .then( res => {
    console.log("RESPONSE !")
    const token = res.data.key
    const expiration_time = new Date(new Date().getTime() + 3600 * 1000);
    window.localStorage.setItem('token', token);
    window.localStorage.setItem('expiration_time', expiration_time);
    dispatch(authSucess(token));
    console.log(token);
    dispatch(checkAuthTimeOut(3600));
  })
  .catch( err => {
    console.log(err);
    dispatch(authFail());
  })
}


export const authStateCheck = () => dispatch => {
  const expiration_time = window.localStorage.getItem('expiration_time');
  const token = window.localStorage.getItem('token');
  if (!token){
    return dispatch(logout());
  } else if ( expiration_time <= new Date()){
    console.log("EXPIRATION");
    console.log( expiration_time <= new Date() )
    return dispatch(logout());
  } else {
    console.log("NEW TIMER !!!! ");
    return checkAuthTimeOut((expiration_time - new Date().getTime()) / 1000)
  }
}

这是我的减速器:

import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from '../../actions/type';


const initialState = {
  error: null,
  token: null,
  loading: false
}

export function authenticationReducer(state = initialState, action){
  switch (action.type) {
    case AUTH_START:
      return {
        ...state,
        loading: true
      }
    case AUTH_SUCESS:
      console.log(action.token);
      console.log(action.loading);
      return {
        ...state,
        error: null,
        token: action.token,
        loading: false
      }

    case AUTH_FAIL:
      return {
        ...state,
        error: action.error
      }
    default:
      return state

  }
}

但是,如果我再点击一次提交,它就会起作用。但我真的希望我的用户立即被重定向。我怎样才能解决这个问题 ??

非常感谢你;(

标签: javascriptreactjsreduxjwt

解决方案


我建议删除 history.push 代码并让 react-router 处理重定向:

import { Route, Redirect } from 'react-router'
...
<Switch>
                    <Route exact
                           path="/:category"
                           render={routerProps => <HomePageStream {...routerProps}/>}
                    />
                    <Route exact
                           path="/article/:slug"
                           render={routerProps => <ArticleDetails {...routerProps}/>}
                    />
                    <Route exact
                           path="/authentication/login"
                           render={ routerProps => (this.props.isAuthenticated ? (<Redirect to="/"/>) :(<Login {...routerProps}{...this.props}/>))}
                    />
</Switch>

推荐阅读