学习【全栈之巅】Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(2.17-2.20)

学习【全栈之巅】Node.js + Vue.js 全栈开发王者荣耀手机端官网和管理后台学习笔记(2.17-2.20)

本项目是 学习Bilibili 全栈之巅 视频教程相关源码和体会
https://gitee.com/blaunicorn/node-vue-wangzherongyao
持续更新中…

2.17 后台登录页面

// admin\src\views\Login.vue
<template>
  <div class="login-container">
    <video
      autoplay
      muted
      loop
      poster="../assets/bg.jpg"
      class="bgvid"
      id="bgvid"
    >
      <!-- <source src="../assets/bg.mp4" type="video/mp4" /> -->
    </video>
    <el-card header="请先登录" class="login-card">
      <el-form label-width="70px" @submit.native.prevent="login">
        <el-form-item label="用户名">
          <el-input
            prefix-icon="el-icon-user-solid"
            v-model="model.username"
            required
            minlength="3"
            maxlength="20"
            placeholder="name"
          ></el-input>
        </el-form-item>
        <el-form-item label="密码">
          <el-input
            type="password"
            prefix-icon="el-icon-s-opportunity"
            clearable
            required
            maxlength="20"
            error
            v-model="model.password"
            placeholder="password"
            minlength="5"
            show-password
          ></el-input>
        </el-form-item>
        <el-form-item
          style="display: flex; justify-content: space-between; flex-wrap: wrap"
        >
          <el-button type="primary" native-type="submit">登录</el-button>
          <el-button type="primary" @click="afterpage">取消</el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        model: {},
      };
    },
    created() {
      this.model = {};
    },
    methods: {
      async login() {
        const res = await this.$http.post('login', this.model);
        console.lo(res);
      },
      afterpage() {},
    },
  };
</script>
<style scoped>
  #bgvid {
    position: fixed;
    right: 0;
    bottom: 0;
    min-width: 100%;
    min-height: 100%;
    width: auto;
    height: auto;
    z-index: -2;
    background: url(../assets/bg.jpg) no-repeat;
    background-size: cover;
  }
  .login-card {
    width: 20rem;
    margin: 5rem auto;
  }
  .login-container {
    text-align: center;
  }
</style>
// server/routes/admin/index.js
    // server端新建登录接口
    app.post('/admin/api/login', async (req, res) => {
        // 结构赋值
        const { username, password } = req.body
        // 1.根据用户名找用户,引入模型,定义方法
        const AdminUser = require('../../models/AdminUser')
        // 简化 {username:username}
        const user = await AdminUser.findOne({ username })
        if (!user) {
            return res.status(422).send({ message: '用户不存在' })
        }
        // 2.校验密码

        // 3.返回token,如果是错误,则在http.js中拦截器统一处理
        res.send({ code: 20000, message: 'ok' })
    })
    admin/http.js
// 统一处理http错误
http.interceptors.response.use(res => {
    return res
}, err => {
    console.log(err, err.response)
    if (err.response.data.message) {
        // console.log(err.response.data.message)
        // 统一处理时, 用vue的实例弹出这个错误
        Vue.prototype.$message({
            type: 'error',
            message: err.response.data.message
        })
    }

    return Promise.reject(err)
})
//  server/routes/admin/index.js
// 优化登录接口,增加密码校验和生成token
    // 登录接口
    app.post('/admin/api/login', async (req, res) => {
        // 结构赋值
        const { username, password } = req.body
        // 1.根据用户名找用户,引入模型,定义方法
        const AdminUser = require('../../models/AdminUser')
        // 简化 {username:username}
        const user = await AdminUser.findOne({ username }).select('+password')
        if (!user) {
            return res.status(422).send({ message: '用户不存在' })
        }
        // 2.校验密码
        // 比较密码,返回布尔值.数据表密码设置select为false不可读取,此时需要强制取出密码 findOne.select('+item')
        const isValid = require('bcryptjs').compareSync(password, user.password)
        if (!isValid) {
            return res.status(422).send({ message: '密码校验错误' })
        }

        // 3.返回token,如果是错误,则在http.js中拦截器统一处理
        // npm i jsonwebtoken
        // 
        const jwt = require('jsonwebtoken')

        // 签名,生成token,加密返回,get会和路由冲突,通过参数的设置让路由进行判断
        const token = jwt.sign({ id: user.id }, app.get('secret'))
        res.send({ code: 20000, message: 'ok', data: { token } })
    })

2.19 继续增加中间件,校验用户是否登录, 此时在前端admin头上添加token

    //前端 http.js
    // 2-19 请求拦截器,增加token header
http.interceptors.request.use(config => {
    //标准授权请求头
    config.headers.Authorization = 'Bearer ' + localStorage.token || ''
    return config
}, error => {
    return Promise.reject(error)
})
        // 后端server/routes/admin/index.js
        // 单独引入 AdminUser模型,供后期调用
    const AdminUser = require('../../models/AdminUser')
    //引用校验token
    const jwt = require('jsonwebtoken')
    router.get('/', async (req, res, next) => {
        const token = String(req.headers.authorization || '').split(' ').pop()
        console.log(token)
        // const tokenData = jwt.verify(token, app.get('secret'), (err, data) => {
        //     console.log(err, data)
        //     if (err && err.message === 'invalid token') return res.send({ message: '无效 token', code: 0 })

        //     if (err && err.message === 'jwt expired') return res.send({ message: 'token 失效', code: 0 })

        //    
        // })
        // tokenData: {id:'String',iat: 'Number'} 既解析出了id
        const { id } = jwt.verify(token, app.get('secret'))
        // // 把用户信息挂载到req上
        req.user = await AdminUser.findById(id)
        console.log(req.user)
        next()

    }, async (req, res) => {})
    // node.js服务端报错使用http-assert,能够很方便的返回错误
    // 2-19 继续增加 npm install http-assert 这个是node.js下判断条件是否成立。用法: assert(确保条件存在,如果不存在抛出什么状态码,信息是什么)

2.20 admin前端还需要对没有axios请求的页面进行登录限制,譬如 /item/create 页面没有请求,后端就限制不到

// router/index.js增加全局前置路由守卫
router.beforeEach((to, from, next) => {
    if (!to.meta.isPublic && !localStorage.token) {
        return next('/login')
    }
    next()
})
// 增加中间件后,上传图片因为没有token header出错,el-upload 增加headers参数
        <el-upload
          class="avatar-uploader"
          :action="$http.defaults.baseURL + '/upload'"
          :headers="headers"
          :show-file-list="false"
          :on-success="afterUpload"
          :before-upload="beforeAvatarUpload"
        >
        ...
        data() {
            return {
                headers: {
                  Authorization: 'Bearer ' + localStorage.token,
                },
            }
        }
// 图片上传headers错误的另一种处理方法:url 和 header混入。
Vue.mixin({
    computed: {
        uploadUrl() {
            return this.$http.defaults.baseURL + '/upload'
        }
    },
    methods: {
        getAuthHeaders() {
            return {
                Authorization: `Bearer ${localStorage.token}`
            }
        }
    }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值