利用redu和react-redu去cookie&localStorage进行登录状态验证的实践

最近使用React和Redux构建一个后台项目,在做登录系统的时候,看了网上很多资料,一般都是使用sessionStorage(包括Cookie,下略)或者localStorage保存从服务器获取的token,然后使用react-router中onEnter这个方法依据sessionStorage或者localStorage中是否存在相应的token来判定登录状态。

Cookie, LocalStorage 与 SessionStorage的详解可以参考:详说 Cookie, LocalStorage 与 SessionStorage一文。

react-router的onEnter方法使用可以参考react-router官方的用例:auth-flow。
仓库地址:https://github.com/reactjs/react-router/tree/master/examples/auth-flow

auth-flow这个用例使用localStorage来保存token,react-router的onEnter调用requireAuth方法来判断auth.loggedIn()是否能正确返回localStorage.token,来维持登录状态,这也是目前常用的做法。

app.js

import React from 'react'import { render } from 'react-dom'import { browserHistory, Router, Route, Link, withRouter } from 'react-router'import auth from './auth'const App = React.createClass({  getInitialState() {    return {      loggedIn: auth.loggedIn()    }  },  updateAuth(loggedIn) {    this.setState({      loggedIn: loggedIn    })  },  componentWillMount() {    auth.onChange = this.updateAuth    auth.login()  },  render() {    return (          1.             {this.state.loggedIn ? (              Log out            ) : (              Sign in            )}          1. About          1. Dashboard (authenticated)        {this.props.children || You are {!this.state.loggedIn && 'not'} logged in.}    )  }})const Dashboard = React.createClass({  render() {    const token = auth.getToken()    return (        # Dashboard        You made it!        {token}    )  }})const Login = withRouter(  React.createClass({    getInitialState() {      return {        error: false      }    },    handleSubmit(event) {      event.preventDefault()      const email = this.refs.email.value      const pass = this.refs.pass.value      auth.login(email, pass, (loggedIn) => {        if (!loggedIn)          return this.setState({ error: true })        const { location } = this.props        if (location.state && location.state.nextPathname) {          this.props.router.replace(location.state.nextPathname)        } else {          this.props.router.replace('/')        }      })    },    render() {      return (           (hint: password1)[br]          login          {this.state.error && (            Bad login information          )}      )    }  }))const About = React.createClass({  render() {    return # About  }})const Logout = React.createClass({  componentDidMount() {    auth.logout()  },  render() {    return You are now logged out  }})function requireAuth(nextState, replace) {  if (!auth.loggedIn()) {    replace({      pathname: '/login',      state: { nextPathname: nextState.location.pathname }    })  }}render((), document.getElementById('example'))

auth.js

module.exports = {  login(email, pass, cb) {    cb = arguments[arguments.length - 1]    if (localStorage.token) {      if (cb) cb(true)      this.onChange(true)      return    }    pretendRequest(email, pass, (res) => {      if (res.authenticated) {        localStorage.token = res.token        if (cb) cb(true)        this.onChange(true)      } else {        if (cb) cb(false)        this.onChange(false)      }    })  },  getToken() {    return localStorage.token  },  logout(cb) {    delete localStorage.token    if (cb) cb()    this.onChange(false)  },  loggedIn() {    return !!localStorage.token  },  onChange() {}}function pretendRequest(email, pass, cb) {  setTimeout(() => {    if (email === 'joe@example.com' && pass === 'password1') {      cb({        authenticated: true,        token: Math.random().toString(36).substring(7)      })    } else {      cb({ authenticated: false })    }  }, 0)}

Web安全方面,我并没有深入研究,但据说localStorage等本地存储容器保存一些用户信息,多少可能会存在XSS注入的风险,那么可不可以不使用这些本地存储来维持用户状态呢?

于是我尝试用redux结合react-router来保持用户的登录状态,最开始的思路是用onEnter调用一个方法来获取store里的登录状态信息,但是发现react-router的路由声明中并不能从store中拿到props,只有路由的history等信息。可能水平有限,只能到处翻文档,无意间在Github中发现一个用例:react-redux-jwt-auth-example

这个用例使用了一个高级函数(high-order function,用例中为requireAuthentication)来包装需要登录权限的Compenent(用例中为ProtectedView),这个Compenent位于所有需要登录权限的顶层:

routers.js

import {HomeView, LoginView, ProtectedView, AView, BView } from '../views';import {requireAuthentication} from '../components/AuthenticatedComponent';export default();

利用requireAuthentication()这个高阶函数将ProtectedView这个Compenent作为参数传人,requireAuthentication()中生成一个Compenent,然后调用react-redux中的connect结合mapStateToProps就能将store中的登录状态,token等信息塞入Props中,当前这个requireAuthentication中的Compenent根据Props中的状态信息来决定是否继续渲染ProtectedView Compenent,或者在用户进行页面跳转,检测到登录状态为false时,就会重定向到登录页面。

AuthenticatedComponent.js

import React from 'react';import {connect} from 'react-redux';import {pushState} from 'redux-router';export function requireAuthentication(Component) {    class AuthenticatedComponent extends React.Component {        componentWillMount() {            this.checkAuth();        }        componentWillReceiveProps(nextProps) {            this.checkAuth();        }        checkAuth() {            if (!this.props.isAuthenticated) {                let redirectAfterLogin = this.props.location.pathname;                this.props.dispatch(pushState(null, `/login?next=${redirectAfterLogin}`));            }        }        render() {            return (                    {this.props.isAuthenticated === true                        ?                         : null                    }            )        }    }    const mapStateToProps = (state) => ({        token: state.auth.token,        userName: state.auth.userName,        isAuthenticated: state.auth.isAuthenticated    });    return connect(mapStateToProps)(AuthenticatedComponent);}

上面是作者给的用法,其中需要注意的是:

import {pushState} from 'redux-router';

由于几个react-router的区别这个问题,打包编译后可能报错,我项目中使用的是react-router-redux。

参考react-router-redux文档中What if I want to issue navigation events via Redux actions?

使用方法是在AuthenticatedComponent中:

import {push} react-router-redux

也就是push代替了原例子中的pushState,作用都差不多。

然后就是加一个中间件:

import { routerMiddleware, push } from 'react-router-redux'// Apply the middleware to the storeconst middleware = routerMiddleware(browserHistory)const store = createStore(  reducers,  applyMiddleware(middleware))

这样就可以在AuthenticatedComponent中愉快地重定向了。

以上利用react-redux,redux-router || react-router-redux基于redux来保持登录状态和进行登录权限验证,可以避免使用Cookie&localStroge等来保存登录信息,其中的缺点就是,用户刷新页面或关闭浏览器后,登录状态就被销毁了,如果有记住用户名等需求,可能依然会用到本地存储容器。

翻阅这个用例最后还发现作者已经写了一个登录权限验证的library:
redux-auth-wrapper

不想搬运代码的兄弟可以参考文档直接拿来用~

第一次写文章,如果有概念错误的地方,请多多指正!!感谢!!

关键字:Redux, react.js, const, import

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部