Nodejs+Express+Vue ——添加token + 封装axios

一、添加 token

阮一峰 JSON Web Token 入门教程

1.原理

1. 登陆时,客户端发送用户名密码
2. 服务端验证用户名密码是否正确,校验通过就会生成一个有时效的token串,发送给客户端
3. 客户端储存token,一般都会存储在localStorage或者cookie里面
4. 客户端每次请求时都带有token,可以将其放在请求头里,每次请求都携带token
5. 服务端验证token,所有需要校验身份的接口都会被校验token,若token解析后的数据包含用户身份信息,则身份验证通过,返回数据

2.利用 nodejs + jwt 生成token 验证token

安装 jsonwebtoken
npm install jsonwebtoken --save
nodejs 服务端

新建 jwt.js 生成token和验证token方法

// 引入模块依赖
const fs = require('fs')
const path = require('path')
const jwt = require('jsonwebtoken')

// 创建 token 类
class Jwt {
  constructor(data) {
    this.data = data
  }

  //生成token
  generateToken() {
    let data = this.data
    let created = Math.floor(Date.now() / 1000)
    let PRIVATE_KEY = fs.readFileSync(path.join(__dirname, './pem/rsa_private_key.pem')) //私钥 可以自己生成
    let token = jwt.sign(
      {
        data,
        exp: created + 60 * 60 * 24 * 3
      },
      PRIVATE_KEY,
      { algorithm: 'RS256' }
    )
    return token
  }

  // 校验token
  verifyToken() {
    let token = this.data
    let PUBILC_KEY = fs.readFileSync(path.join(__dirname, './pem/rsa_public_key.pem')) //公钥 可以自己生成
    let res
    try {
      let result = jwt.verify(token, PUBILC_KEY, { algorithms: ['RS256'] }) || {}
      let { exp = 0 } = result,
        current = Math.floor(Date.now() / 1000)
      if (current <= exp) {
        res = result.data || {}
      }
    } catch (e) {
      res = 'err'
    }
    return res
  }
}

module.exports = Jwt

rsa_public_key和private_key生成 OpenSSL生成私钥和公钥和使用非对称加密颁发验证token

登录时 生成token 并返回给客户端

const JwtUtil = require('./jwt') // 引入jwt token工具

// 登录
const login = async (request, response) => {
  const q1 = `SELECT * FROM userinfo WHERE name = $1`
  const q2 = `SELECT * FROM userinfo WHERE name = $1 AND password = $2`
  try {
    let values = [request.body.username, request.body.password]
    let res1 = await pool.query(q1, [values[0]])
    let res2 = await pool.query(q2, values)
    if (res1.rowCount !== 0) {
      if (res2.rowCount !== 0 && res2.rows[0].password === values[1]) {
        // 登陆成功,添加token验证
        let user = { id: res2.rows[0].id, name: res2.rows[0].name }
        // 将用户传入并生成token
        let jwt = new JwtUtil(user)
        let token = jwt.generateToken()
        response.status(200).json({
          status: true,
          message: '登录成功!',
          token: token,
          results: { id: res2.rows[0].id, username: res2.rows[0].name }
        })
      } else {
        response.status(400).json({ status: false, message: '账号密码错误!' })
      }
    } else {
      response.status(404).json({ status: false, message: '账号不存在!' })
    }
  } catch (err) {
    response.status(500).json({ status: false, message: '账号密码错误!' })
  }
}

入口文件index.js中 需要验证身份的接口进行请求时 要验证token

const JwtUtil = require('./jwt')

app.use(function (req, res, next) {
  // 登陆和注册请求不验证,其他的多有请求需要进行token校验
  if (req.url != '/login' && req.url != '/register') {
    let token = req.headers.authorization
    // let token = req.headers.authorization.replace('Bearer ', '') // postman token测试
    // console.log('token:', token)
    let jwt = new JwtUtil(token)
    let result = jwt.verifyToken()
    // 如果考验通过就next,否则就返回登陆信息不正确
    if (result == 'err') {
      res.status(403).send({ status: false, message: '登录已过期,请重新登录' })
    } else {
      next()
    }
  } else {
    next()
  }
})
客户端

store中的index.js (其中isLogin可不看)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  // 设置属性
  state: {
    isLogin: false,
    token: ''
  },
  // 获取属性的状态
  getters: {
    //获取登录状态
    isLogin: state => state.isLogin
  },
  // 设置属性状态
  mutations: {
    //保存登录状态
    userStatus(state, flag) {
      state.isLogin = flag
    },
    set_token(state, ltoken) {
      // localStorage.setItem('token', ltoken)
      state.token = ltoken
    },
    del_token(state) {
      // localStorage.removeItem('token')
      state.token = ''
    }
  },
  // 应用mutations
  actions: {
    userLogin(context, flag) {
      context.commit('userStatus', flag)
    },
    set_token(context, token) {
      context.commit('set_token', token)
    },
    del_token(context) {
      context.commit('del_token')
    }
  },
  modules: {}
})

export default store


Login.vue登录 存放token

 submit() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          const params = {
            username: this.loginForm.username,
            password: this.loginForm.password
          }
          login(params)
            .then(res => {
              console.log(res)
              if (res.status) {
                //使用vuex对全局token进行状态管理
                // this.$store.dispatch('set_token', res.token)
                localStorage.setItem('token', res.token)

                //设置Vuex登录标志为true,默认userLogin为false
                this.$store.dispatch('userLogin', true)
                //Vuex在用户刷新的时候userLogin会回到默认值false,所以我们需要用到HTML5储存
                //我们设置一个名为Flag,值为isLogin的字段,作用是如果Flag有值且为isLogin的时候,证明用户已经登录了。
                localStorage.setItem('Flag', 'isLogin')
                this.$message.success(res.message)

                const { id, username } = res.results
                localStorage.setItem('UserInfo', JSON.stringify({ id: id, username: username }))
                this.$router.push({ path: '/home' })
              } else {
                this.$message.error(res.message)
              }
            })
        } else {
          return false
        }
      })
    },

示例连接:
vue & nodejs jwt 的基于token身份验证
nodejs 基于token的身份验证
JWT_nodeJs_express_vue 实例

二、封装 axios

比较完整的 vue中axios的封装

安装axios

npm install --save axios
在src下 创建文件夹request存放http.jsapi.js

http.js (只封装的通用的get和post,可以根据自己的需求进行封装,修改headers 也可添加到参数中)

axios.defaults.baseURL的三种配置方法

import axios from 'axios'
import router from '../router/index'
import store from '../store/index'
import { Message } from 'element-ui'

// 消息提示函数
const tip = msg => {
  Message({
    message: msg,
    type: 'error',
    duration: 2000,
    showClose: true
  })
}

// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
const toLogin = () => {
  router.replace({
    path: '/login',
    query: {
      redirect: router.currentRoute.fullPath
    }
  })
}

// 环境的切换
if (process.env.NODE_ENV == 'development') {
  axios.defaults.baseURL = 'http://10.52.184.226:3000'
} else if (process.env.NODE_ENV == 'debug') {
  axios.defaults.baseURL = ''
} else if (process.env.NODE_ENV == 'production') {
  axios.defaults.baseURL = ''
}

axios.defaults.timeout = 1000 * 60 * 3 // 请求超时时间
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8' // post请求头

// 请求拦截器
axios.interceptors.request.use(
  config => {
    // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
    // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    const token = localStorage.getItem('token')
    store.dispatch('set_token', token)
    if (token) {
      config.headers.Authorization = token
    }
    return config
  },
  error => {
    return Promise.error(error)
  }
)

// 响应拦截器
axios.interceptors.response.use(
  response => {
    if (response.status === 200) {
      return Promise.resolve(response)
    } else {
      return Promise.reject(response)
    }
  },
  // 服务器状态码不是200的情况
  error => {
    if (error.response.status) {
      switch (error.response.status) {
        // 401: 未登录
        // 未登录则跳转登录页面,并携带当前页面的路径
        // 在登录成功后返回当前页面,这一步需要在登录页操作。
        case 401:
          toLogin()
          break
        // 403 token过期
        // 清除本地token和清空vuex中token对象 并 跳转登录页面
        case 403:
          store.dispatch('del_token')
          localStorage.removeItem('token')
          setTimeout(() => {
            toLogin()
          }, 1000)
          break
        // 其他错误,直接抛出错误提示
        default:
          tip(error.response.data.message)
      }
      return Promise.reject(error.response)
    }
  }
)
/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function get(url, params) {
  return new Promise((resolve, reject) => {
    axios
      .get(url, {
        params: params
      })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function post(url, params) {
  return new Promise((resolve, reject) => {
    axios
      .post(url, params)
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}

api.js

import { get, post } from './http'

export const login = p => post('/login', p)
export const register = p => post('/register', p)
export const getUserInfo = p => post('/getUserInfo', p)
export const cbmProperty = () => get('/data/cbmProperty')

封装接口调用示例——getUserInfo

import { getUserInfo } from '@/request/api'
 getUserInfo() {
      const params = { id: this.userInfo.id }
      getUserInfo(params)
        .then(res => {
          if (res.status) {
            this.userInfo.username = res.username
          }
        })
    }
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值