Freeman 的博客

React服务端渲染如何读取登录状态

May 06, 2015

在使用React构建应用的过程中,如何判断当前的登录状态,是每个应用都要遇到的问题,而Client端渲染,常用的JWT模式,对于token的保存,一般都是保存在localStorage中,而在服务端渲染中,是没有localStorage的定义的

 typeof localStorage === 'undefined' // true

既然我们无法从localStorage中获取数据,那么我们只能从Cookie中获取

    //after login 
    cookie.save('token', token)

在服务端渲染的express应用中,我们要引入 cookie-parser模块

const express = require('express')
const cookieParser = require('cookie-parser') ;
const serverRender = require('./server.js')

const app = express()

app.use(cookieParser())

//other codes  

app.get('*', function (req, res, next) {
    serverRender.default(req, res);
})
app.listen(port, function(err) {
    if (err) {
        console.error(err)
    } else {
        console.info("==> 🌎  Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port)
    }
})
import React from 'react'
import { renderToString } from 'react-dom/server'
import { RouterContext, match, createMemoryHistory } from 'react-router'
import configureStore from '../store/configureStore'
import routes from '../routes'

export default function render (req, res) {
  const token = req.cookies['token'] // get token from req.cookies
  let initStore = {}
  if (token && isExp(token)) { // token 存在并且是有效未超时的
    initStore = {user: {token: token}}
  }
  const history = createMemoryHistory()
  const store = configureStore(initStore, history)

  match({routes: routes(store), location: req.url}, (error, redirectLocation, renderProps) => {
    // other codes 
  })

}

上述代码中关键的部分就是express应用中使用cookie-parse模块把cookie放到req.cookies中,在请求中再从req.cookies中获取token, 这时候校验token是否有效。有效则把token存放到初始的store中

下一步则是在路由中添加登录验证钩子


import React from 'react'
import { Route, IndexRoute } from 'react-router'
import { redirectToLogin, redirectToBack,isExp } from './utils/userUtils'
import { isClient } from './utils'
import App from './containers/App'
import Login from './views/login'
import Index from './views/Index'
import NotFound from './components/NotFound'
import Mine from './views/Mine'

export default (store) => {

  const serverRequireAuth = (nextState, replace) => {

    const {user: {token}} = store.getState()
    if (!token || !isExp(token)) {
      replace('/login')
    }
  }

  const serverRedirectAuth = (nextState, replace) => {
    const {user: {token}} = store.getState()
    if (token && isExp(token)) {
      replace('/')
    }
  }
  //客户端渲染和服务端渲染的校验方式可能会不同
  const requireAuth = isClient() ? redirectToLogin : serverRequireAuth
  const redirectAuth = isClient() ? redirectToBack : serverRedirectAuth

  return (
    <Route path="/" component={App}>
      <IndexRoute component={Index}/>
      <Route path="/login" component={Login} onEnter={redirectAuth}/>
      <Route path="mine" component={Mine} onEnter={requireAuth}/>
      <Route path="*" component={NotFound}/>
    </Route>
  )
}

客户端判断登录状态的函数

import jwtDecode from 'jwt-decode'
import moment from 'moment'
import { getCookie, setCookie, removeCookie} from './authService'

const getInfoFromToken = (token) => {
  return jwtDecode(token)
}

function checkTokenExpDiff (token) {
  let tokenPayload = getInfoFromToken(token)
  let expiry = moment.unix(tokenPayload.exp)
  return expiry.diff(moment(), 'seconds')
}
/**
 * 是否登录
 * token 存在,并且有效期 > 0秒
 * @returns {boolean} 已经登录返回true,否则返回false
 */
const isLogin = function () {
  const apiToken = getCookie('token')
  return apiToken && isExp(apiToken)
}
/**
 *
 * @param apiToken
 * @return {boolean} token有效返回true,否则返回false
 */
const isExp = function (apiToken) {
  return checkTokenExpDiff(apiToken) > 0
}


const redirectToBack = (nextState, replace) => {
  //已经登录则不进入
  if (isLogin()) {
    replace('/')
  }
}
const redirectToLogin = (nextState, replace) => {
  if (!isLogin()) {
    replace('/login')
  }
}

export {
  redirectToLogin,
  redirectToBack,
}

京ICP备19052401号

「Powered by CloudBase Webify