jwt鉴权(react express jsonwebtoken)

17 篇文章 0 订阅

此文记录jwt鉴权(react express jsonwebtoken)思路流程

整个项目可以在https://github.com/furfur-jiang/DevConnector处查看,欢迎start

后端四步

1. 配置一个随机内容,全局使用

config/default.json

{
  "jwtSecret": "mysecrettoken",
}

2. 登录接口,返回token

routes/api/auth.js

const express = require('express')
const router = express.Router()
const auth = require('../../middleware/auth')
const jwt = require('jsonwebtoken')
const User = require('../../models/User')
const { body, validationResult } = require('express-validator')
const config = require('config')
const bcrypt = require('bcryptjs')
router.post(
  '/',
  [
    //检验输入内容
    body('email', '请输入正确邮箱').isEmail(),
    body('password', '请输入密码').exists(),
  ],
  async (req, res) => {
    const errors = validationResult(req)
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() })
    }
    const { email, password } = req.body

    try {
      let user = await User.findOne({ email: email })
      if (!user) {
        return res.status(400).json({ error: [{ msg: 'Invalid Credentials 没有登录资格' }] })
      }

      const isMatch = await bcrypt.compare(password, user.password)
      
      if (!isMatch) {
        return res.status(400).json({ error: [{ msg: 'Invalid Credentials 没有登录资格' }] })
      }

      const payload = {
        user: {
          id: user.id,
        },
      }

      jwt.sign(
        payload,
        config.get('jwtSecret'), //从config文件获取内容
        { expiresIn: 360000 },
        (err, token) => {
          if (err) throw err
          res.json({ token })
        },
      )
      //return jwt
    } catch (err) {
      console.error(err.message)
      res.status(500).send('Server error')
    }
  },
)

// 加auth中间件,接受携带token请求,返回用户内容
router.get('/', auth, async (req, res) => {
  try {
    const user = await User.findById(req.user.id).select('-password')
    res.json(user)
  } catch (err) {
    console.error(err.message)
    res.status(500).send('Server Error')
  }
})

module.exports = router

3. 后端添加鉴权中间件,解析后返回解码后的内容,便于后续中间件使用

middleware/auth.js

const jwt = require('jsonwebtoken')
const config = require('config')

module.exports = function (req, res, next) {
  //从头部获取token
  const token = req.header('x-auth-token')
  //判断是否有token
  if (!token) {
    return res.status(401).json({ msg: '没有访问权限' })
  }
  try {
    //解析token
    const decoded = jwt.verify(token, config.get('jwtSecret'))
    req.user = decoded.user //赋予解码后的user属性,便于后续使用
    next()
  } catch (err) {
    res.status(401).json({ msg: '令牌无效' })
  }
}

4. 需要权限的接口,加上auth中间件即可

/**
 * @route GET api/profile/me
 * @desc  Get current users profile
 * @access Private
 */
router.get('/me', auth, async (req, res) => {
  try {
    const profile = await Profile.findOne({
      user: req.user.id,
    }).populate('user', ['name', 'avatar'])

    if (!profile) {
      return res.status(400).json({ msg: '此用户没有配置文件' })
    }
    res.json(profile)
  } catch (err) {
    console.error(err.message)
    res.status(500).send('Server Error')
  }
})

前端五步

1. 前端登录后,存储token、用户信息、登录态 到state和localstorge

src/actions/auth.js

// load user
export const loadUser = () => async (dispatch) => {
  if (localStorage.token) {
    setAuthToken(localStorage.token)//加x-auth-token头部,函数定义在下面
  }
  try {
    const res = await api.get('/auth')
    dispatch({
      type: USER_LOADED,
      payload: res.data,
    })
  } catch (error) {
    dispatch({
      type: AUTH_ERROR,
    })
  }
}

//Login user
export const login = (email, password) => async (dispatch) => {
  const body = JSON.stringify({ email, password })
  try {
    const res = await api.post('/auth', body)
    dispatch({
      type: LOGIN_SUCCESS,
      payload: res.data,
    })
    dispatch(loadUser())
  } catch (err) {
    const errors = err.response.data.errors
    if (errors) {
      errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')))
    }
    dispatch({
      type: LOGIN_FAIL,
    })
  }
}

2. 将token设置到请求头

src/utils/setAuthToken.js

import api from './api';

const setAuthToken = token => {
  if (token) {
    api.defaults.headers.common['x-auth-token'] = token
    localStorage.setItem('token', token);
  }else{
    delete api.defaults.headers.common['x-auth-token']
    localStorage.removeItem('token');
  }
}

export default setAuthToken

3. 定义对应reducer

src/reducers/auth.js

function auth(state = initialState, action) {
  const { type, payload } = action
  switch (type) {
    case USER_LOADED:
      return {
        ...state,
        isAuthenticated: true,
        loading: false,
        user:payload
      }
    case REGISTER_SUCCESS:
    case LOGIN_SUCCESS:
      localStorage.setItem('token', payload.token)
      return {
        ...state,
        ...payload,
        isAuthenticated: true,
        loading: false,
      }
    case AUTH_ERROR:
    case REGISTER_FAIL:
    case LOGIN_FAIL:
    case LOGOUT:
    case ACCOUNT_DELETE:
      localStorage.removeItem('token')
      return { ...state, token:null, isAuthenticated: false, loading: false }
    default:
      return state
  }
}

export default auth

4. 封装私有路由

src/components/routing/PrivateRoute.js

import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { Navigate } from 'react-router-dom';
import Spinner from '../layout/Spinner';

const PrivateRoute = ({
  component: Component,
  auth: { isAuthenticated, loading }
}) => {
  if (loading) return <Spinner />;
  if (isAuthenticated) return <Component />;

  return <Navigate to="/login" />;
};
PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
}
const mapStateToProps = (state) => ({
  auth: state.auth,
})
export default connect(mapStateToProps)(PrivateRoute)

5. 使用私有路径,私有路由就会判断是否已授权

<Provider store={store}>
	<Router>
		<Routes>
			<Route
			   path="/posts/:id"
			   element={<PrivateRoute component={Post} />}
		</Routes>
	</Router>
</Provider>

附依赖版本

后端
“dependencies”: {
“bcryptjs”: “^2.4.3”,
“config”: “^3.3.7”,
“express”: “^4.17.2”,
“express-validator”: “^6.14.0”,
“gravatar”: “^1.8.2”,
“jsonwebtoken”: “^8.5.1”,
“mongoose”: “^6.1.8”,
“request”: “^2.88.2”
}
前端
“dependencies”: {
“@testing-library/jest-dom”: “^5.16.2”,
“@testing-library/react”: “^12.1.2”,
“@testing-library/user-event”: “^13.5.0”,
“axios”: “^0.25.0”,
“moments”: “0.0.2”,
“react”: “^17.0.2”,
“react-dom”: “^17.0.2”,
“react-moment”: “^1.1.1”,
“react-redux”: “^7.2.6”,
“react-router-dom”: “^6.2.1”,
“react-scripts”: “5.0.0”,
“redux”: “^4.1.2”,
“redux-devtools-extension”: “^2.13.9”,
“redux-thunk”: “^2.4.1”,
“uuid”: “^8.3.2”,
“web-vitals”: “^2.1.4”
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值