Vue2电商平台(六)、注册登录,请求头配置token,token持久化存储;导航守卫(重点);组件内守卫、路由独享守卫

news2024/11/25 15:41:06

文章目录

  • 一、注册
    • 1. 获取注册验证码
    • 2. 完成注册用户
  • 二、登录
    • 1. 登录获取token
    • 2. Home首页携带token获取用户数据
    • 3. 持久化存储token
    • 4. 退出登录
    • 5. 导航守卫 (牛)
    • 6. 路由独享守卫beforeEnter
    • 7. 组件内守卫(用的很少)

一、注册

1. 获取注册验证码

本系统:点击按钮,发送请求,后台返回生成的验证码。(正常的业务逻辑是:点击按钮,后台根据手机号给手机发送验证码,用户输入验证码。)
在这里插入图片描述

1. 接口
// 获取验证码// /api/user/passport/sendCode/{phone}
export const reqRegisterCode = function (phone) {
  return requests.get(`/user/passport/sendCode/${phone}`);
}
2. vuex小仓库
// 新建user小仓库,用来存储登录注册的信息
import { reqRegisterCode, registUser } from '@/api'
export default {
  namespaced: true,
  state: {
    code: ''
  },
  actions: {
    // 获取验证码
    async getCode (context, phone) {
      let res = await reqRegisterCode(phone)
      if (res.code === 200) {
        context.commit('GETCODE', res.data)
        return 'ok'
      } else {
        return Promise.reject(new Error('faile'))
      }
    },
  mutations: {
    GETCODE (state, data) {
      state.code = data
    }
  },
  getters: {},
}
3. Register(注册)组件内
async getRegisterCode () {
  try {
    //1. 发请求 来个逻辑短路判断手机号是否输入
    this.phone && (await this.$store.dispatch('user/getCode', this.phone))
    // 2. 获取到验证码,方式一: 
    this.code = this.$store.state.user.code
    // 方式二是用computed ...mapState('user', ['code'])
  } catch (error) {
    alert(error.message)
  }
},

v-model将验证码code双向绑定到验证码的输入框里。当后台返回验证码,组件内读取到验证码时,验证码就自动输入到input框里。

2. 完成注册用户

拿着手机号、密码、验证码去发请求,将该用户添加到数据库中。

1. 接口
// 注册用户   /api/user/passport/register  请求方法 post 
// 参数 phone:手机号,password:密码; code:验证码
export const registUser = (phone, password, code) => {
  return requests(
    {
      url: `/user/passport/register`,
      method: 'post',
      data: {
        phone, password, code
      }
    })
}
2.vuex
aciotns:{
 // 注册用户
 async sendRegister (context, { phone, password, code }) {
   let res = await registUser(phone, password, code)
   // 223 注册失败
   console.log('注册结果', res);
   if (res.code === 200) {
     return 'ok'
   } else {
   // 不同类型的失败,在res.message中都会给出解释
     return Promise.reject(new Error(res.message))
   }
 }
}

在这里插入图片描述
注册成功则跳转到登录页面

3. 组件
data () {
  return {
    phone: '',      // 电话号码
    code: '',      //验证码
    password: '',      // 登录密码
    rePassowrd: '',      // 确认密码
    isAgree: true      // 是否同意
  }
}
methods:{
 async registUser () {
  console.log('注册用户');
   try {
     const { phone, code, password, rePassowrd, isAgree } = this
     // 逻辑与验证信息
     if (phone && code && password && rePassowrd && password == rePassowrd && isAgree == true) {
       await this.$store.dispatch('user/sendRegister', { phone, password, code })
     }
     // 注册成功路由需要进行跳转,跳转到登录页面
     this.$router.push({
       path: '/login',
     })
   } catch (error) {
   // 这里的message接收的是vuex中的Promise.reject()里的值
     alert(error.message)
   }
 }
}

在这里插入图片描述
这部分还缺一个表单验证,老师用的vee-validate。ElementUI有现成的表单验证,也可以用这个。本系统就先不弄表单验证了。

二、登录

1. 登录获取token

1. 登录接口
// 登录 /api/user/passport/login  请求:方法post
export const reqUserLogin = (phone, password) => {
  return requests({
    url: '/user/passport/login',
    method: 'post',
    data: {
      phone,
      password
    }
  })
}
2. vuex存储数据
state: {
    code: '', // 验证码
    token: ''
  },
actions:{
   // 登录
  async userLogin (context, { phone, password }) {
    let res = await reqUserLogin(phone, password)
    console.log('登录结果', res);
    // 服务器下发token作为用户的唯一标识,前端经常通过携带token找服务器要一些用户的数据进行展示
    if (res.code === 200) {
      context.commit('USERLOGIN', res.data.token)
      return 'ok'
    } else {
      return Promise.reject(res.message)
    }
  }
},
 mutations: {
 USERLOGIN (state, token) {
   state.token = token
 }
}

打印请求结果。登录成功的时候,后台为了区分用户是谁,服务器会下发token作为唯一标识符。这里的登录接口做的并不好,应该只返回token。
在这里插入图片描述
需要注意:vuex存储token并非持久化存储。

4. 组件发登录请求,成功则跳转home页面(暂定是home页面)
methods: {
  async Login () {
    console.log('登录');
    try {
      await this.$store.dispatch('user/userLogin', { phone: this.phone, password: this.pwd })
      //  成功则跳转到首页【结合导航守卫,这句话后续会变】
      this.$router.push('/home')
    } catch (error) {
      alert(error.message)
    }
  }
}

一般的业务逻辑为登录成功,服务器下发token,前端持久化存储token。前端找服务器要用户数信息进行展示时需要带着token

这里存在一个问题:登录成功之后一定是跳转到首页吗?具体看导航守卫,会回来填坑的。

2. Home首页携带token获取用户数据

登录成功,进入首页时,应该获取用户信息并展示在页面上。
在这里插入图片描述
(1) 获取用户信息的接口

// 获取用户信息 /api/user/passport/auth/getUserInfo
export const reqUSerInfo = () => {
  return requests({
    url: '/user/passport/auth/getUserInfo',
    method: 'get',
  })
}

(2). vuex三连环存储用户信息

state: {
  code: '',
  token: '',
  userInfo: {}
},
actions:{
 // 获取用户信息
  async getUserInfo (context) {
    let res = await reqUSerInfo()
  }
},
mutations:{
    GETUSERINFO (state, data) {
    state.userInfo = data
  }
}
   

(3). 请求拦截器携带token

// 2. 配置请求拦截器: 请求拦截器检测到请求,在请求发出去之前做一些事情
import store from '@/store'
requests.interceptors.request.use((config) => {
  // 进度条开始
  nProgress.start()
  // config:配置对象,其中header请求头属性很重要
  config.headers.userTempId = store.state.detail.userTempId
  // 配置token请求头
  if (store.state.user.token) {
    config.headers.token = store.state.user.token
  }
  return config;
})

Home组件一挂载完毕就发送获取用户信息的请求。

4. Home组件发请求
mounted () {
  // 发送请求,获取用户信息,【结合导航守卫,这格请求以后不在这里执行】
  this.$store.dispatch('user/getUserInfo')
},

在这里插入图片描述
如果不携带token,发送获取用户信息的请求时,服务器的返回结果是 未登录
在这里插入图片描述
(5). 将用户信息渲染到界面上

 <!-- 登录成功 则显示用户信息-->
 <p v-show="userName">
   <span>{{ userName }}</span>
   <router-link to="/login" class="register">退出登录</router-link>
 </p>
 <!-- 未登录则显示请登录等信息 -->
 <p v-show="!userName">
   <span></span>
   <router-link to="/login">登录</router-link>
   <router-link to="/register" class="register"
     >免费注册</router-link   >
 </p>
<script>
 computed: {
  userName () {
    return this.$store.state.user.userInfo.name
  }
}
</script>

在这里插入图片描述

3. 持久化存储token

当登录成功、进入首页、获取用户信息成功渲染到界面上后。此时如果刷新页面,会发现控制台仍旧报未登录的错误。这是因为,Vuex存储token并不是持久化存储。刷新页面,token数据清空,而Home页重新加载,仍旧发请求获取用户信息,没有token所以会报错。解决办法就是持久化存储token,即本地存储。

方式一:仓库中存储、仓库中读取
在这里插入图片描述
请求拦截器中读取仓库里的token,在请求头里加上token
在这里插入图片描述
方式二:仓库中存储,请求拦截器里读取
在这里插入图片描述
在这里插入图片描述
这里存在一个问题:首页发送获取用户信息的请求的前提是登录成功、跳转到首页。而在刚进入网站时,路由重定向的就是首页,所以刚进入网站时,就会有一个未登录的错误。(实际中,刚进入网页时不应该请求用户的信息。这个在导航守卫中被解决)

4. 退出登录

退出登录:,
(1)发请求,通知服务器退出登录
(2)清除项目当中的数据 userInfotoken

1. 接口
// 退出登录 /api/user/passport/logout
export const reqUserLogout = () => {
  return requests({
    url: '/user/passport/logout',
    method: 'get',
  })
}

此处注意action里面不能操作state,提交mutation修改state

2. vuex
actions: {
  // 退出登录
  async LogOut (context) {
    let res = await reqUserLogout()
    if (res.code === 200) {
     //action里面不能操作state,提交mutation修改state
      context.commit('CLEAR')
      return 'ok'
    } else {
      return Promise.reject(new Error('falie'))
    }
  }
},
mutations: {
  CLEAR (state) {
    // 清空用户数据
    state.userInfo = {}
    // 仓库里的token清空
    state.token = ''
    // 本地存储清空
    localStorage.removeItem('TOKEN')
  }
}
3. Header 组件内
 // 退出登录
 async userLogOut () {
   try {
     // 请求成功,则跳转页面
     await this.$store.dispatch('user/LogOut')
     // 回到首页
     this.$router.push('/login')
   } catch (error) {
     alert(error.message)
   }
 }

这里跳回首页会有一个bug

5. 导航守卫 (牛)

路由守卫的具体内容回顾这篇博客:Vue(十三) 路由守卫
导航:表示路由正在发生变化。导航守卫就是在进行路由跳转时进行的一些操作。

之前的程序存在的问题:
Q1: 用户在登录之后,地址栏中输入/login,仍旧能够进入登录页面。
Q2: 在刚进入网站时,重定向到首页,首页会请求用户信息,若此时还未登录,则没有token,请求会报错。
Q3: 由于获取用户信息的请求是在Home模块。假如进入search页面后,刷新页面,虽然有token,但是Search模块并不会发送请求,所以页面的用户信息还是会丢失。
Q4: 登录成功就一定跳转Home首页吗?

这些问题都是在路由变化的情况下发生的。导航守卫:

在这里插入图片描述
Q2,Q3----请求放在导航守卫里,不放在首页了。无论跳转哪个页面,即使是Search页面,如果没有用户信息,就会发起请求,获取数据并放到仓库重。Header组件再去读取仓库里存储的用户信息。

Q4----比如用户点击我的订单,但是此时未登录,则先去登录页面。登录成功之后应该直接跳转到我的订单页面,而不是首页。

// router/index.js
router.beforeEach(async (to, from, next) => {
 // 判断是否登录
 let token = localStorage.getItem('TOKEN')
 // 1. 已登录
 if (token) {
   // 2. 已登录,又要去登录或注册页面,不让去,就待在原来的页面
   if (to.path === '/login' || to.path === '/register') {
     next(false) // 取消当前的导航,url地址会重置到from路由对应的地址
   } else {
     // 2. 已登录,不去登录或注册页面,判断是否有用户信息
     // 3. 有用户信息,放行
     if (store.state.user.userInfo.name) {
       next()
     } else {
       // 3. 没有用户信息,发请求
       try {
         // 发送请求,获取用户信息
         await store.dispatch('user/getUserInfo')
         // 获取成功,放行
         next()
       } catch (error) {
         // 获取不成功,但是又登录了,说明token失效,则退出登录,并跳转到登录页面
         await store.dispatch('user/LogOut')
         next('/login')
       }
     }
   }
 } else {
   // 未登录访问, 交易相关(trade)、支付相关(pay,paysuccess)、用户中心(center)相关跳转到 登录页面; (此处感觉用路由meta元信息也可以)
   if (to.path.indexOf('/trade') != -1 || to.path.indexOf('/pay') != -1 || to.path.indexOf('/center') != -1) {
     // 跳到登录页面去,并携带这个想去的路由地址
     next('/login?redirect=' + to.path)
   } else {
     // 去的不是上面这些路由,而是home|search|shopCart等别的路由,放行
     next()
   }
 }
})

而在登录组件中,登录成功跳转的页面为:
在这里插入图片描述

未登录,点击我的订单,跳转到登录页面,此时路由信息为:
在这里插入图片描述

6. 路由独享守卫beforeEnter

当用户登录之后,还有如下几条规则需要设置:
(1) 只有从购物车页面(shopCart)才能跳转到交易页面(创建订单)
(2) 只有从交易页面(创建订单)才能跳转到支付页面(pay)

 // 交易
  {
    name: 'trade',
    path: '/trade',
    component: Trade,
    beforeEnter (to, from, next) {
      /* 只能从购物车界面跳转到交易界面 */
      if (from.path == './shopCart') {
        next()
      } else {
        next(false)
      }
    },
  },
  // 支付
  {
    name: 'pay',
    path: '/pay',
    component: Pay,
    /* 只能从交易界面跳转到支付界面 */
    beforeEnter (to, from, next) {
      if (from.path == './trade') {
        next()
      } else {
        next(false)
      }
    }
  }

7. 组件内守卫(用的很少)

熟悉一下这几个API

export default {
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
        console.log(this) //undefined
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

beforeRouteUpdate这个API测试了一下,当路径由http://localhost:8080/#/detail/12直接变为http://localhost:8080/#/detail/14(也就是后边参数改变)时,这个API会被调用。如果由详情页(detail/12)返回商品页再进入详情页(detail/14);这个API也不会被调用。

只有从支付页面(pay)才能跳转到支付成功页面(paysuccess)。

export default {
  name: 'PaySuccess',
  beforeRouteEnter (to, from, next) {
    if (from.path === './pay') {
      next()
    } else {
      next(false)
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2195206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM+小程序的线上教育商城管理系统(教育2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 微信小程序线上教育商城有管理员&#xff0c;教师&#xff0c;学生三个角色。 1、管理员功能有个人中心&#xff0c;学生管理&#xff0c;教师管理&#xff0c;类型管理&#xff0c;课程…

diffusion model(1.1) 为什么前向传播和反向传播都遵循高斯分布?

DDPM的引用[53]为Deep Unsupervised Learning using Nonequilibrium Thermodynamics&#xff0c;它证明了当 β t \beta_t βt​ 较小时&#xff0c;前向传播和反向传播前向分布和后向分布拥有同样的分布形式。 所引论文的内容如下&#xff0c; 后者又引用了另一篇论文(Fell…

分层解耦-04.IOCDI-IOC详解

一.Bean的声明 springboot为了更好地标识web应用开发中bean对象到底归属于哪一层&#xff0c;在Component注解的基础上又衍生出了三个注解Controller、Service和Repository。分别应用于Controller层&#xff0c;Service层和Dao层。推荐使用衍生注解&#xff0c;当无法判断该be…

Java-数据结构-反射、枚举 |ू・ω・` )

目录 ❄️一、反射&#xff1a; 1、定义&#xff1a; 2、用途&#xff1a; 3、反射相关的类&#xff1a; 4、Class类&#xff1a; 1、Class类中相关的方法&#xff1a; 5、Class反射实例&#xff1a; 1、获得Class对象的三种方式&#xff1a; 2、反射的使用&#xff1a; 6、反…

网络基础擅长组建乐队

让我们荡起双桨 来说说网络吧 现有计算机要进行协作&#xff0c;网络的产生是必然的 局域网&#xff1a;计算机数量更多了, 通过交换机和路由器连接在一起 广域网&#xff1a;将远隔千里的计算机都连在一起 交换机路由器等设备就应运而生 计算机是人的工具&#xff0c;人要协…

layernorm笔记

文章目录 layer norm的解释二维三维 batchnorm和layernorm主要的区别为什么要在序列转录模型中使用layer norm&#xff1f; layer norm的解释 二维 红色为batchnorm&#xff0c;蓝色为layer norm batchnorm对每一个特征算均值和方差 layer norm对每一个批次算均值和方差 三…

窗口系统与图形绘制接口

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 当我们想要进行底层图形应用(GUI)开发时,往往需要用到窗口系统和图形库,这里简单介绍一下 视窗系统(window system)与通信协议 下面内容主要针对Unix-like操作系统 视窗系统是以使用视窗作为主要特征之一的图形用…

【含开题报告+文档+PPT+源码】基于SSM + Vue的养老院管理系统【包运行成功】

开题报告 随着社会的发展和经济的进步&#xff0c;人口老龄化问题逐渐凸显。统计数据显示&#xff0c;全球范围内的老龄人口比例正在逐年上升&#xff0c;养老需求也随之增长。养老院作为提供专业养老服务的机构&#xff0c;承担着照料老人、提供医疗保健和社交活动等责任。传…

DVWA Brute Force篇暴力破解 Low/Medium/Hight

这里就不再赘述环境搭建&#xff0c;就默认你已经做好环境搭建的准备了.接下来就步入正题(我的bp是汉化) 任务一&#xff0c;设置级别为low级别&#xff0c;创建好我们的字典文件 任务二 开启抓包&#xff0c;并且我们输入错误的账户密码 &#xff08;当我知道账户名称为admin时…

Nginx06-静态资源部署

零、文章目录 Nginx06-静态资源部署 1、静态资源概述 静态资源&#xff1a;是在Web开发中不经常改变的文件&#xff0c;比如图片、CSS样式表、JavaScript脚本文件等。这些资源通常是预先编译好的&#xff0c;不需要服务器端的动态处理。动态资源&#xff1a;是在Web开发中需…

fiddler抓包19_模拟IP地址(如异地IP)

课程大纲 使用场景举例&#xff1a;北京模拟海南IP地址“182.118.99.12”发送请求。 ① 打开CustomRules.js文件&#xff1a;Fiddler快捷键“CtrlR”(或鼠标点击&#xff0c;菜单栏 - Rules“规则” - Customize Rules“自定义规则”)。 ② 添加代码&#xff0c;保存&#xff1…

SpringBoot在线教育系统:架构设计与技术选型

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

计算机网络——DNS

提供域名——>ip地址的转换 分层命名 分布式解析 怎么样维护 DNS实现在边缘系统——传输层及其以上 还提供别名——>规范名字的转换——>ip 负载均衡是一种用于在多个计算资源&#xff08;如服务器、网络设备等&#xff09;之间分配工作负载的技术。其主要目的是…

最小生成树prim算法kruskal算法

最小生成树 在一个无向图中求一棵树(n-1条边&#xff0c;无环&#xff0c;连通所有点)而且这棵树的边的权和最小 prim(普利姆)算法 prim算法有叫加点法&#xff0c;我们先标定一个点&#xff0c;然后寻找与这个点相连的边的权值最小的点&#xff0c;不断重复此操作&#xff…

【鸿蒙学习】深入解析鸿蒙应用与元服务:含义、区别、应用场景及创建方法

文章目录 鸿蒙应用&#xff08;HarmonyOS App&#xff09;含义用于干什么优缺点 元服务&#xff08;Atomic Service&#xff09;含义用于干什么优缺点 鸿蒙应用与元服务的区别创建方法鸿蒙应用的创建元服务的创建 总结 随着科技的不断进步&#xff0c;操作系统也在不断迭代更新…

医院管理自动化:Spring Boot技术实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

上传文件失败,请检查阿里云配置信息:[The specified bucket is not valid.

-- 十一假期结束 -- 去年今日此门中&#xff0c;人面挑花相应红。 -- 人面不知何处去&#xff0c;桃花依旧笑春风。

UART驱动学习三(TTY驱动部分源码解析)

目录 全局框架图一、tty_io.c 分析1. 关键数据结构和定义2. 文件操作结构体3. 初始化和注册4. 读写操作5. 挂起和恢复6. 信号处理7. 设备类8. 控制台通知9. 辅助函数10. 代码功能11. 带有注释的部分tty_io.c源码 二、tty_ldisc.c 分析1. 关键数据结构和定义2. 行规程操作函数3.…

从零开始打造华丽的国庆生活记录本地HTML网站

目录 目录 前言 准备工作 所需工具 文件夹结构 基础知识入门 HTML基础 CSS基础 JavaScript基础 步骤详解 1. 创建项目文件夹 2. 编写HTML文件 3. 添加CSS样式 4. 增加交互功能 5. 添加图片和视频 6. 美化网站 7. 实现响应式设计 8. 测试与优化 附加功能 创建…