功能:登录和退出(表单重置和预验证、路由导航守卫、路由重定向)

3.登录/退出功能

3.1登录概述

3.1-1.登录业务流程

①在登录页面输入用户名和密码

②调用后台接口进行验证

③通过验证之后,根据后台的响应状态跳转到项目主页

3.1-2.登录业务的相关技术点
  • http 是无状态的(需要记录用户的登录状态)
    • 通过 cookie 在客户端记录状态
    • 通过 session 在服务器端记录状态
    • 通过 token 方式维持状态

3.2登录–token原理分析

1.如果前端和后台接口之间不存在跨域问题,推荐使用Cookie或者session记录登录状态。
2.如果前端和后台接口存在跨域问题,则使用token。
分析:token原理
	现在有客户端和服务器
	服务器:专门提供API接口。如登录接口
	客户端:需要通过ajax请求,访问服务器上的数据
	目前:客户端与服务器之间存在跨域问题
token原理:
1.客户端在登录页面输入用户名和密码,把这次请求提交到服务器。
2.服务器根据用户的这次请求验证用户是否存在。
  如果登录成功之后,服务器向客户端返回一个唯一的token值。
3.客户端需要把服务器返回的token值,记录到客户端本地。
4.今后如果客户端要请求服务器的API接口,必须携带token才能够正常的进行操作。
  如果客户端在发起下一次请求的时候,携带了token。
5.服务器接收到token之后,验证token是否存在:如果存在证明用户已登录
  然后会根据携带的token验证是哪个用户,从而根据用户的操作返回不同的结果。

注意事项:
1.token值是由服务器生成,而且每个不同的用户生成的token值也是不一样的。
2.token值记录了用户的登录状态
3.token:客户端与服务器端进行身份校验

3.3登录功能实现

1.登录页面的布局

通过Element–UI组件实现布局

  • el-form
  • el-form-item
  • el-input
  • el-button
  • 字体图标
找到自己的项目 vue_shop
打开vscode,在项目根目录vue_shop运行:git status

注意:在开发中只有开发新功能了,尽量把功能放在新的分支上进行开发;分支开发完成以后再把这个分支合并到master分支上

​ 1.创建分支命令:git checkout -b 分支名

​ 2.在项目根目录,运行:git checkout -b login

​ 3.查看所有分支,运行:git branch 分支前存在* 表示当前处于这个分支 *login

梳理vue项目的结构,删除不必要的内容
  • 1.App.vue根组件
<template>
  <div id="app">
    APP根组件
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
</style>
  • 2.路由 router.js
import Vue from 'vue'              指的是引入vue.js文件
import Router from 'vue-router'
Vue.use(Router)                    Vue.use() 明确地安装路由功能

export default new Router({
    routes: [

    ]
})
  • 3.删除views文件夹
  • 4.删除components/HelloWorld.vue文件
功能:创建登录组件,通过路由的形式渲染到App根组件,添加路由重定向redirect
  • 1.在components/新建登录组件:Login.vue
<template>
    <div>
        登录组件
    </div>
</template>

<script>
export default {
    
}
</script>

<style lang="less" scoped>

</style>

注意:
1.lang="less"  支持less语法格式
2.scoped是vue的指令。控制组件样式生效的区间
	如果加上scoped,样式只在当前组件内生效
	如果去掉scoped,样式会全局生效

一个组件它的样式是来美化自己组件结构的,不应该影响其他的组件
所以:只要定义的是单文件组件,一定为style标签加上scoped指令
	  避免组件之间的样式冲突
  • 2.在路由 router.js 导入路由–添加路由规则
import Vue from 'vue'
import Router from 'vue-router'
import Login from '../components/Login.vue'

Vue.use(Router)

export default new Router({
    routes: [{
        path: '/',
        redirect: '/login'
    }, {
        path: '/login',
        component: Login
    }]
})
  • 3.在根组件 App.vue,添加路由占位符
<template>
  <div id="app">
    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>
功能:完成登录组件的基本页面布局:全屏背景色–登录的盒子

实现全屏背景色

  • 1.在src/assets/新建css文件夹/新建global.css
html,
body,
#app {
    height: 100%;
    margin: 0;
    padding: 0;
}
  • 2.在入口文件main.js中导入全局样式表:import ‘./assets/css/global.css’
  • 3.在Login.vue中,为style标签添加 height: 100%;
<style lang="less" scoped>
   .login_container{
       height: 100%;
       background-color: #2b4b6b;
   }
</style>

登录的盒子 login_box

在Login.vue文件中:

<template>
    <div class="login_container">
        <div class="login_box">

        </div>
    </div>
</template>

<script>
export default {
    
}
</script>

<style lang="less" scoped>
   .login_container{
       height: 100%;
       background-color: #2b4b6b;
   }
   .login_box{
       width: 450px;
       height: 300px;
       background-color: #fff;
       border-radius: 3px;
       position: absolute;
       top: 50%;
       left: 50%;
       transform: translate(-50%,-50%)
   }
</style>

功能:绘制图像区域 avatar_box
<template>
    <div class="login_container">
        <div class="login_box">
            <div class="avatar_box">
                <img src="../assets/logo.png" alt="">
            </div>
        </div>
    </div>
</template>

<script>
export default {
    
}
</script>

<style lang="less" scoped>
   .login_container{
       height: 100%;
       background-color: #2b4b6b;
   }
   .login_box{
       width: 450px;
       height: 300px;
       background-color: #fff;
       border-radius: 3px;
       position: absolute;
       top: 50%;
       left: 50%;
       transform: translate(-50%,-50%);

       .avatar_box{
           height: 130px;
           width: 130px;
           border: 1px solid #eee;
           border-radius: 50%;
           padding: 10px;
           box-shadow: 0 0 10px #ddd;
           position: absolute;
           left: 50%;
           transform: translate(-50%,-50%);
           background-color: #fff;
           img{
               width: 100%;
               height: 100%;
               border-radius: 50%;
               background-color: #eee;
           }
       }
   }
</style>
功能:登录的表单区域

打开Element-UI官网,粘贴对应的html结构

因为我们的组件是按需导入的,所以如果用到form表单、input输入框等

  • 1.需要先在src/plugins/element.js,引入相关的组件
// 按需导入
import { Button } from 'element-ui'
import { Form, FormItem } from 'element-ui'
import { Input } from 'element-ui'

// 注册全局组件
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItemon)
Vue.use(Input)
  • 2.在Login.vue文件中: Form表单–Button按钮–Input输入框
<template>
    <div class="login_container">
        <div class="login_box">
            <!-- 头像区域 -->
            <div class="avatar_box">
                <img src="../assets/logo.png" alt="">
            </div>
            <!--登录表单区域 -->
            <el-form class="login_form">
                <!-- 用户名区域 -->
                <el-form-item>
                    <el-input></el-input>
                </el-form-item>
                <!-- 密码区域 -->
                 <el-form-item>
                    <el-input></el-input>
                </el-form-item>
                <!-- 按钮区域 -->
                <el-form-item class="btns">
                    <el-button type="primary">登录</el-button>
                    <el-button type="info">重置</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

<script>
export default {
    
}
</script>

<style lang="less" scoped>
   .login_container{
       height: 100%;
       background-color: #2b4b6b;
   }
   .login_box{
       width: 450px;
       height: 300px;
       background-color: #fff;
       border-radius: 3px;
       position: absolute;
       top: 50%;
       left: 50%;
       transform: translate(-50%,-50%);

       .avatar_box{
           height: 130px;
           width: 130px;
           border: 1px solid #eee;
           border-radius: 50%;
           padding: 10px;
           box-shadow: 0 0 10px #ddd;
           position: absolute;
           left: 50%;
           transform: translate(-50%,-50%);
           background-color: #fff;
           img{
               width: 100%;
               height: 100%;
               border-radius: 50%;
               background-color: #eee;
           }
       }
   }
   .login_form{
       position: absolute;
       bottom: 0;
       width: 100%;
       padding: 0 20px;
       box-sizing: border-box;
   }
   .btns{
       display: flex;
       justify-content: flex-end;
   }
</style>
功能:绘制密码和小图标
  • 1.在Element-UI官网查找:输入框–带 icon 的输入框
 <el-input  prefix-icon="el-icon-search"></el-input>    el-icon-search 需要被替换为 iconfont icon-user

因为Element-UI官网找不到需要的图标,所以借助第三方图标库。

下载好之后,将字体图标文件fonts,放在assets文件夹里面 assets/fonts

  • 2.然后在main.js导入
// 导入字体图标
import './assets/fonts/iconfont.css'
  • 3.在Login.vue中,iconfont 图标名
 <el-input prefix-icon="iconfont icon-user"></el-input>       用户头像
 <el-input prefix-icon="iconfont icon-3702mima"></el-input>   用户密码锁
功能:实现表单的数据绑定(element文档提供的做法)
  • 1.为el-form标签添加model 属性绑定到数据对象form :model = ‘form’
  • 2.为每一个表单项,通过v-model绑定到数据对象上的属性中
<template>
    <div class="login_container">
        <div class="login_box">
            <!-- 头像区域 -->
            <div class="avatar_box">
                <img src="../assets/logo.png" alt="">
            </div>
            <!--登录表单区域 -->
            <el-form :model="loginForm" class="login_form">
                <!-- 用户名区域 -->
                <el-form-item>
                    <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user">

                    </el-input>
                </el-form-item>
                <!-- 密码区域 -->
                 <el-form-item>
                    <el-input v-model="loginForm.password" prefix-icon="iconfont icon-3702mima" type="password">

                    </el-input>
                </el-form-item>
                <!-- 按钮区域 -->
                <el-form-item class="btns">
                    <el-button type="primary">登录</el-button>
                    <el-button type="info">重置</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

<script>
export default {
    data(){
        return {
            // 这是登录表单的数据绑定对象
            loginForm: {
				username: 'wuddong',
				password: '123'
			}
        }
    }
}
</script>
功能:表单的数据验证(这是element官网文档提供的做法)
  • 为el-form标签通过绑定rules属性,指定校验对象(自定义) :rules="loginFormRules"
  • 在data数据中定义校验对象:loginFormRules,其中每一个属性就是一个验证规则(数组)
  • 为不同的表单el-form-item项,通过prop指定不同的验证规则进行表单验证

在Login.vue文件中:

第一步:<el-form :model="loginForm" :rules="loginFormRules" class="login_form"></el-form>

第二步:在data数据中

			// 这是表单的验证规则对象
			(required: true  表示必填项;trigger: 'blur' 表示鼠标离开进行验证)

            loginFormRules:{
                // 验证用户名是否合法
                username: [
                    { required: true, message: '请输入用户名', trigger: 'blur' },
                    { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
                ],
                // 验证密码是否合法
                password: [
                    { required: true, message: '请输入密码', trigger: 'blur' },
                    { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
                ]
            }

第三步:
用户名区域  <el-form-item prop="username"></el-form-item>
密码区域    <el-form-item prop="password"></el-form-item>
功能:表单的重置—点击重置按钮,重置表单的效验–resetFields
Vue直接操作DOM

	1.通过ref标注DOM元素
    // 在DOM元素上通过ref属性标注,属性名称自定义  <div ref="info">hello</div>
    
    2.通过$refs获取DOM元素
    	// 通过Vue实例的$refs获取标记ref属性的元素
    	let info = this.$refs.info.innerHTML
    	console.log(info) // hello

方法:查询官方文档form–Form Methods 拿到表单的实例对象Form,调用resetFields函数

  • 为表单el-form添加ref引用,ref引用指定的值就是表单的引用对象loginFormRef

  • 为重置按钮添加事件 @click=“resetLoginForm”,触发resetLoginForm事件处理函数

  • 在resetLoginForm事件处理函数中,通过this.$refs.loginFormRef.resetFields()

    在Login.vue文件中

第一步:登录表单区域  <el-form ref="loginFormRef"> </el-form>
第二步:<el-button type="info" @click="resetLoginForm">重置</el-button>
第三步:在script标签定义methods方法
 methods:{
        // 点击重置按钮,重置登录表单
        resetLoginForm(){
            console.log(this); 
			//结果是组件的实例对象,里面有个属性$refs
            this.$refs.loginFormRef.resetFields();
        }
    }
功能:实现登录前表单数据的预验证–validate

方法:查询官方文档form–Form Methods 拿到表单的引用对象,调用validate函数

  • 获取表单的引用对象loginFormRef

  • 拿着引用对象loginFormRef,调用validate函数进行表单预校验;

    在validate中它接收一个回调函数,第一个形参是验证的结果:布尔值

    第一步:为登录按钮添加点击事件  <el-button type="primary" @click="login">登录</el-button>
    第二步:在script标签的methods方法中
     login(){
      this.$refs.loginFormRef.validate(valid=>{
    	  console.log(valid);
      })
    }
    
功能:配置axios发起登录请求

1.如果要请求服务器:

- 启动MySql服务器(Phpstudy服务器)
- 找到素材/vue-api-server/app.js,在窗口启动:node  app.js

2.在main.js中:配置axios (重点)

​```js
// 导入axios包
import axios from 'axios'
// 配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
// 把axios包挂载到vue原型对象上:这样每一个vue组件都可以通过this直接访问到$http,从而去发起ajax请求
Vue.prototype.$http = axios

3.根据验证的结果valid,判断是否发起登录请求

如果请求成功,Login.vue

  • 请求方式:post
  • 请求地址:login
  • 请求参数:this.loginForm
  login(){
            this.$refs.loginFormRef.validate(async valid=>{
                console.log(valid); 
				//valid是布尔值:true 或者 false
				//******************内容如下:
                if(!valid) return;   不发起请求
                const {data: res} = await this.$http.post("login",this.loginForm);
                console.log(res);
                // {data: null, meta: {…}}
                if(res.meta.status !== 200) return console.log('登录失败');
                console.log('登录成功');
            })
        }
    }

因为post请求的结果是promise实例对象,所以在函数使用 await async
这样结果是:服务器返回的数据是一个对象:{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
		  **es6语法:取对象里面的属性值**
	      我们只需要里面的data对象,所以结构复制出data属性重命名为res对象{data: res}  res就是data对应的属性值
然后根据res的meta属性,判断是否登录成功  res.meta.status !== 200
功能:配置Message全局弹框组件
需求:登录成功或者失败,给用户友好提示

配置方法:

  • 从element-ui中导入弹框组件 import { Message } from 'element-ui'
  • 把弹框组件挂载到vue的原型对象上 Vue.prototype.$message = Message
  • 调用$message提供的方法

查看官方文档:Message消息提示

plugins/element.js中,导入弹框提示组件:

// 导入弹框提示组件
import { Message } from 'element-ui'
Vue.prototype.$message = Message  

注意:
1.$message是自定义属性
2.Vue.prototype.$message = Message  
  表示把弹框组件挂载到vue的原型对象上,这样每一个组件都可以通过this访问到$message
  然后就可以弹框提示了
  同时$message身上提供了一些弹框方法error('失败')、success('成功')
  我们只需要调用这些方法,提示需要的信息就可以了。
  

Login.vue中,调用这些方法:

  登录成功:this.$message.error('登录失败')
  登录失败:this.$message.success('登录成功')
2.实现登录

​ ①通过 axios 调用登录验证接口

​ ②登录成功之后保持用户 token 信息

​ ③跳转到项目主页

在Login.vue中:

 this.$message.success('登录成功')
    // 1.将登录成功之后的token,保存到客户端的sessionStorage中
    //   localstorage   是持久化的存储机制
    //   sessionStorage 是会话期间的存储机制
    //      1.1 项目中除了登录之外的其他API接口,必须在登录之后才能访问
    //      1.2 token只应在当前网站打开期间生效,所以将token保存在sessionStorage中
第一步: window.sessionStorage.setItem("token",res.data.token);
	// 2.通过编程式导航跳转到后台主页,路由地址是 /home
第二步: this.$router.push('/home');

然后在components文件夹下,新建Home.vue

<template>
    <div>
        Home 组件
    </div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>

</style>

接着在router.js导入home组件,添加路由规则

// 导入home组件
import Home from './components/Home.vue'
// 添加路由规则
{ path: '/home',component: Home }
3.路由导航守卫控制访问权限

需求:/home页面属于有权限的页面

  1. 只有用户在登录的情况下才允许访问,如果是未登录状态是不允许看到home页面的
  2. 如果用户没有登录,希望用户从home路径直接跳转到登录页

如果用户没有登录,但是直接通过URL访问特定页面,需要重新导航到登录页面

**在router.js中,添加路由导航守卫 router.beforeEach **

 // 为路由对象,添加 beforeEach 函数(导航守卫) 
   to表示将要访问的页面路径;
   from表示从哪个路径跳转到哪个路径
   next表示放行的函数
   
 const router = new Router({})
 router.beforeEach((to, from, next) => {
   
   // 如果用户访问的登录页,直接放行允许访问登录页
   if (to.path === '/login') return next()
   // 如果访问的不是登录页,判断sessionStorage 中是否存在 token 值
   const tokenStr = window.sessionStorage.getItem('token')
   // 没有token,证明你没有登录,强制跳转到登录页
   
   if (!tokenStr) return next('/login')
   // 经过判断用户有token值,直接放行
   next()
 })
 
 export default router
 
 **通过用户访问的地址以及用户是否有token值,来决定用户具体访问的是哪个页面。**

退出功能实现原理

基于 token 的方式实现退出比较简单,只需要销毁本地的 token ,利用编程式导航–重新返回登录页面。

这样,后续的请求就不会携带 token ,必须重新登录生成一个新的 token 之后才可以访问页面。

在Home.vue组件中:

<template>
    <div>
       第一步:<el-button type="info" @click="logout">退出</el-button>
    </div>
</template>

<script>
	export default {
第二步:methods:{
        	logout(){
            	window.sessionStorage.clear();
            	this.$router.push("/login");
        	}
    	}
	}
</script>

<style lang="less" scoped>

</style>

登录页面 login.vue

完整代码示例:

<template>
  <dir class="login_container">
    <div class="login_box">
      <!-- 头部图片区域 -->
      <div class="avator_box">
        <img src="../assets/logo.png" alt />
      </div>
      <!-- 表单区域 -->
      <el-form class="login_form" :model="loginForm" :rules="loginFormRules" ref="loginFormRef">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="username">
          <el-input
            type="password"
            v-model="loginForm.password"
            prefix-icon="iconfont icon-3702mima"
          ></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </dir>
</template>

<script>
export default {
  data () {
    return {
      // 表单的数据绑定
      loginForm: {
        username: 'admin',
        password: '123456'
      },
      // 表单的数据验证
      loginFormRules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    // 重置按钮
    resetLoginForm () {
      console.log(this)
      this.$refs.loginFormRef.resetFields()
    },
    // 登录按钮
    login () {
      // 实现登录前表单的预验证
      this.$refs.loginFormRef.validate(async valid => {
        console.log(valid)
        if (!valid) return
        // 验证通过--执行登录操作
        const { data: res } = await this.$http.post('/login', this.loginForm)
        console.log(res)
        if (res.meta.status !== 200) return this.$message.error('登录失败')
        this.$message.success('登录成功')
        // 登录成功:保存token、跳转主页
        window.sessionStorage.setItem('token', res.data.token)
        this.$router.push('/home')
      })
    }
  }
}
</script>

<style lang="less" scoped>
.login_container {
  height: 100%;
  background-color: #006695;
  .login_box {
    width: 450px;
    height: 300px;
    background-color: #fff;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    .avator_box {
      width: 130px;
      height: 130px;
      border: 1px solid #ccc;
      border-radius: 50%;
      padding: 10px;
      box-shadow: 0 0 10px #ddd;
      background-color: #fff;
      position: absolute;
      left: 50%;
      transform: translate(-50%, -50%);
      img {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        background-color: #eee;
      }
    }
  }
  .login_form {
    position: absolute;
    bottom: 0;
    width: 100%;
    padding: 0 10px;
    box-sizing: border-box;
    .btns {
      display: flex;
      justify-content: flex-end;
    }
  }
}
</style>

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花流雨

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值