react通过react-router-dom拦截实现登录验证

在使用react开发项目中,有些页面需要登录之后才能访问,所以需要进行拦截,此处分享采用react-router-dom v4+redux+redux-saga+ant-mobile+axios技术来实现

Login.jsx

import React from "react";
import { is, fromJS } from "immutable";
import { connect } from "react-redux";
import { PropTypes } from "prop-types";
import { login } from "../../store/login/action";
import { InputItem, Button, List } from "antd-mobile";

class Login extends React.Component {
  static propTypes = {
    loginInfo: PropTypes.object.isRequired,
    login: PropTypes.func.isRequired
  };
  constructor(props) {
    super(props);
    this.state = { name: "", psd: "" };
  }
  //antd-mobile的InputItem必须用List组件包裹,其输入值就是value
  username = value => {
    this.setState({
      name: value
    });
  };
  password = value => {
    this.setState({
      psd: value
    });
  };
  toLogin = () => {
    if (this.state.name === "") {    
        // to do sth
    } else if (this.state.psd === "") {
      // to do sth
    } else {
      let data = {};
      data.username = this.state.name;
      data.password = this.state.psd;
    // 触发action
      this.props.login(data);
    }
  };
  componentWillReceiveProps(nextProps) {
      if (Object.keys(nextProps.loginInfo).length > 0) {
        if (nextProps.loginInfo.result.code === 10000) {
          // 登录成功
          sessionStorage.setItem(
            "userinfo",
            JSON.stringify(nextProps.loginInfo.data[0])
          );
          setTimeout(() => {
            let RedirectUrl = nextProps.location.state
              ? nextProps.location.state.from.pathname
              : "/";
            nextProps.history.push(RedirectUrl);
          }, 200);
        } else {

        }
      }
  }
  shouldComponentUpdate(nextProps, nextState) {
    return (
      !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
    );
  }
  componentWillUpdate(nextProps, nextState) {}
  componentWillMount() {}
  render() {
    return (
      <div >
        <div>
          <List>
            <InputItem
              type="text"
              placeholder="请输入用户名"
              onChange={this.username}
              value={this.state.name}
            />
            <InputItem
              type="password"
              placeholder="请输入密码"
              onChange={this.password}
              value={this.state.psd}
            />
          </List>

          <Button type="primary" onClick={this.toLogin}>登录</Button>
        </div>
      </div>
    );
  }
  componentDidMount() {}
  componentDidUpdate() {}
  componentWillUnmount() {}
}

export default connect(
  state => ({
    loginInfo: state.loginInfo.userinfo
  }),
  { login }
)(Login);

utils/asyncCompontent.jsx

import React from 'react'
export default function asyncComponent(importComponent){
    class AsyncComponent extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                component:null
            };
        }
        async componentDidMount(){
            const {default:component} = await importComponent();
            this.setState({component});
        }
        render(){
            const C = this.state.component;
            return C ? <C {...this.props}/> : null;
        }
    }
    return AsyncComponent;
}

router/index.js

import React from "react";
import { HashRouter, Route, Redirect, Switch } from "react-router-dom";
import { PrivateRoute } from "./auth"; //需要登录的路由
import asyncComponent from "../utils/asyncComponent";//按需加载
import app from "../App";
const example = asyncComponent(() =>import("../components/antiDesign/example"));
const login = asyncComponent(() => import("../components/login/login"));
const noMatch = asyncComponent(() => import("../components/noMatch/noMatch"));

class RouteConfig extends React.Component {
  render() {
    return (
      <HashRouter>
        <Switch>
          <Route path="/" exact component={app} />
          <PrivateRoute path="/example" component={example} />
          <Route path="/login" component={login} />
          <Route component={noMatch} />
        </Switch>
      </HashRouter>
    );
  }
}

export default RouteConfig;

router/auth.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";

export const PrivateRoute = ({ component: ComposedComponent, ...rest }) => {
  class Authentication extends Component {
    render() {
      let isLogin= this.props.isLogin
        ? this.props.isLogin
        : sessionStorage.getItem("userinfo")
          ? sessionStorage.getItem("userinfo")
          : "";
      return (
        <Route
          {...rest}
          render={props =>
            !isLogin? (
              <Redirect
                to={{
                  pathname: "/login",
                  state: { from: props.location }
                }}
              />
            ) : (
              <ComposedComponent {...props} />
            )
          }
        />
      );
    }
  }

  const AuthenticationContainer = connect(state => ({
    isLogin: state.loginInfo.isLogin
  }))(Authentication);
  return <AuthenticationContainer />;
};

store/store.js

import { createStore, combineReducers, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import createSagaMiddleware from 'redux-saga'
import logger from 'redux-logger'
import * as login from './login/reducer'
import rootSaga from './sagas'
const sagaMiddleware = createSagaMiddleware();
const middlewares = [ sagaMiddleware, logger];
let store = createStore(
    combineReducers(login),
    composeWithDevTools(applyMiddleware(...middlewares))
);
sagaMiddleware.run(rootSaga);
export default store;

store/sagas/index.js

import { takeEvery, takeLatest, call, put, all } from 'redux-saga/effects';
import * as loginPro from '../login/action-types';
import {  login } from '../../service/api';

// worker saga
// Login
function* getLoginInfo(action) {
    try {
        const userInfo = yield call(login, action.param);
        if (userInfo.data.result.code === 10000) {
            yield put({ type: loginPro.LOGIN_INFO, userinfo: userInfo.data, isLogin: true })
        } else {
            yield put({ type: loginPro.LOGIN_INFO, userinfo: userInfo.data, isLogin: false })
        }

    } catch (e) {
        yield put({ type: loginPro.LOGIN_FAILIURE, error: e })
    }
}

// wacther saga
function* takeLogin() {
    yield takeLatest(loginPro.LOGIN_SUCCESS, getLoginInfo)
}



// root saga
export default function* rootSaga() {
    yield takeLogin()
}

store/login/action-types.js

export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_INFO = 'LOGIN_INFO';
export const LOGIN_FAILIURE = 'LOGIN_FAILIURE';

store/login/action.js

import * as pro from './action-types'

export const login = (param) => {
    return (dispatch) => dispatch({
        type: pro.LOGIN_SUCCESS,
        param
    })
}

store/login/reducers.js

import * as pro from './action-types'
let defaultState = {
    userinfo: {},
    error: {}
}
export const loginInfo = (state = defaultState, action) => {
    switch (action.type) {
        case pro.LOGIN_INFO:
            return {...state, ...action }
        case pro.LOGIN_FAILIURE:
            return {...state, ...action }
        default:
            return state;
    }
}

service/api.js

import {instance} from './apiConfig';

export const login = (data) => {
    return instance.post('/login', data)
}

service/apiConfig.js

import axios from 'axios';
import Qs from 'qs';
import { Toast } from 'antd-mobile'
// 全局默认配置

// 设置 POST 请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

// 在向服务器发送前,修改请求数据(只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法)
// 采用Qs的方式是为了用于application/x-www-form-urlencoded
axios.defaults.transformRequest = [(data) => { return Qs.stringify(data) }]


// 在传递给 then/catch 前,允许修改响应数据
// axios.defaults.transformResponse = [(data) => { return JSON.parse(data) }]

// 配置 CORS 跨域
// 表示跨域请求时是否需要使用凭证
axios.defaults.withCredentials = true;

axios.defaults.crossDomain = true;

// 设置超时
axios.defaults.timeout = 40000;


// 拦截器的说明
// 1、interceptor必须在请求前设置才有效。
// 2、直接为axios全局对象创建interceptor, 会导致全局的axios发出的请求或接收的响应都会被拦截到, 所以应该使用axios.create() 来创建单独的axios实例。

let instance = axios.create({
        baseURL: '',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        withCredentials: true,
    })


// Add a request interceptor
instance.interceptors.request.use(function(config) {

    // POST 请求参数处理成 axios post 方法所需的格式        
    if (config.method === 'post' || config.method === "put" || config.method === "delete") {
        //config.data = JSON.stringify(config.data);
    }
    Toast.loading('Loading...', 0, () => {
        console.log('Load complete !!!');
    });
    return config;
}, function(error) {
    // Do something with request error
    Toast.hide()
    return Promise.reject(error);
});

// Add a response interceptor
instance.interceptors.response.use(function(response) {
    // Do something with response data
    Toast.hide()
    return response.data;
}, function(error) {
    // Do something with response error
    Toast.hide()
    return Promise.reject(error);
});



export { instance };

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store/store";
import Route from "./router/";
import registerServiceWorker from "./registerServiceWorker";
import { AppContainer } from "react-hot-loader";


const render = Component => {
  ReactDOM.render(
    //绑定redux、热加载
    // Provider作为顶层组件,提供数据源,然后可以源源不断的从它向下流到各级子孙节点上去,所以Redux把store注册到Provider中
    // Provider接受一个属性store,这就是我们全局的数据store。我们要根据reducer函数来创建它
    <Provider store={store}>
      <AppContainer>
        <Component />
      </AppContainer>
    </Provider>,
    document.getElementById("root")
  );
};

render(Route);

// Webpack Hot Module Replacement API
if (module.hot) {
  module.hot.accept("./router/", () => {
    render(Route);
  });
}
registerServiceWorker();

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值