目录
一 登录概述
1 登录业务流程
a 在登录页面输入用户名和密码
b 调用后台接口进行验证
c 通过验证之后,根据后台的响应状态跳转到项目主页
2 登录业务的相关技术点
-
http 是无状态的
-
通过 cookie 在客户端记录状态
-
通过 session 在服务端记录状态
-
通过 token 方式维持状态
3 token 和 cookie/session 一般选型策略
当前端和后端项目存在跨域,推荐使用token方式。
当前端和后端项目不存在跨域,推荐使用cookie和session技术。vue前后端使用的是跨域,一般使用token。
4 路由导航守卫控制访问权限
如果用户没有登录,但是直接访问URL访问特定的页面,需要重新导航到登录页面。
5 退出功能实现原理
基于 token 方式实现退出比较简单,只需要销毁本地的 token 即可,后续请求就不会携带 token,必须重写登录生成一个新的 token 之后才可以访问页面。
二 登录—— token 原理分析
三 登录功能实现
登录页面的布局
通过 Element-UI 组件实现布局,会用到下面组件:
- el-form
- el-form-item
- el-input
- el-button
- 字体图标
这些组件布局后的效果如下:
四 梳理项目结构
1 清理 APP.vue组件
<template><div id="app"></div></template><script>export default {name: 'app'}</script><style></style>
2 清理路由文件 index.js
import Vue from 'vue'import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = []const router = new VueRouter({routes})export default router
3 删除不必要的文件
- About.vue
- Home.vue
- HelloWorld.vue
五 安装less-Loader和less依赖
六 将阿里的图标库放到下面目录
F:\vue\vue_shop\src\assets
文件从: https://gitee.com/cakin24/vue-shop-admin/tree/master/src/assets 获取
七 代码
1 新建登录组件 Login.vue
<!--结构--><template><div class="login_container"><!-- 登录盒子 --><div class="login_box"><!-- 头像区域 --><div class="avatar_box"><img src="../assets/logo.png" alt=""></div><!-- 登录表单区 --><!-- 用 ref 表示表单的实例对象 用 :model 进行表单属性绑定,用 :rules 绑定表单验证规则 --><el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form"><!--用户名--><!--prop指定校验规则--><el-form-item prop="username"><!-- 使用的是阿里的图标库 用 v-model 进行数据双向绑定--><el-input prefix-icon="iconfont icon-user" v-model="loginForm.username"></el-input></el-form-item><!--密码--><el-form-item prop="password"><!-- 使用的是阿里的图标库,密码框用show-password --><el-input prefix-icon="iconfont icon-3702mima" v-model="loginForm.password"show-password></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></div></template><!--行为--><script>export default {name: "Login",data() {return {// 这是登录表单的数据表单对象loginForm: {username: 'admin',password: '123456'},// 这是表单的验证规则对象loginFormRules: {// 验证用户名是否合法username: [{required: true, message: '请输入登录名称', trigger: 'blur'},{min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur'}],// 验证密码是否合法password: [{required: true, message: '请输入登录密码', trigger: 'blur'},{min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur'}]}}},methods: {// 点击重置按钮,重置登录表单:resetFieldsresetLoginForm() {this.$refs.loginFormRef.resetFields();},// 登录预验证:validatelogin() {this.$refs.loginFormRef.validate(async (valid) => {if (!valid) return;// data 重命名为resconst {data: res} = await this.$http.post('login', this.loginForm);if (res.meta.status !== 200) return this.$message.error("登录失败!")this.$message.success("登录成功!")/** 1 将登录成功之后的 token,保存到客户端的 sessionStorage 中*1.1 项目中除了登录之外的其他API接口,必须在登录之后才能访问*1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中* 2 通过编程式导航跳转到后台主页,路由地址是 /home* */// 将 token 保存到 浏览器的 [Application] -> [sessionStorage] 中window.sessionStorage.setItem('token', res.data.token)this.$router.push('/home')})}}}</script><!--样式--><!-- scoped:只在当前组件中生效 --><style lang="less" scoped>.login_container {background-color: #2b4b6b;/* 使得 login_container 占满全屏 */height: 100%;}.login_box {width: 450px;height: 300px;background-color: #fff;/* 圆角边框 */border-radius: 3px;/* 盒子定位,使得盒子位于屏幕的正中间 */position: absolute;left: 50%;top: 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;}/* 跳转button的位置 */.btns {display: flex;justify-content: flex-end;}</style>
2 修改路由 index.js
import Vue from 'vue'import VueRouter from 'vue-router'// 导入登录组件import Login from "../components/Login";import Home from "../components/Home";import {renderThumbStyle} from "element-ui/packages/scrollbar/src/util";Vue.use(VueRouter)const routes = [// 路由重定向,当访问/,就会重定向到/login{path: '/',redirect: '/login'},// 定义登录路由规则{path: '/login',component: Login},// 定义Home的路由规则{path: '/home',component: Home}]const router = new VueRouter({routes})// 挂载路由导航守卫// to 将要访问的路径// from 代表从哪个路径跳转而来// next 是个函数,表示放行 next() 放行next('/login') 强制跳转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')next()})export default router
3 修改APP.vue,添加路由占位符
<template><div id="app"><!-- 路由占位符--><router-view></router-view></div></template><script>export default {name: 'app'}</script><style></style>
4 新建全局样式 global.css
/* 全局样式 *//* 撑满全屏 */html, body, #app {height: 100%;margin: 0;padding: 0;}
5 修改入口文件main.js
import Vue from 'vue'import App from './App.vue'import router from './router'import './plugins/element.js'// 导入阿里的字体图标import './assets/fonts/iconfont.css'// 导入全局样式表,使得全局样式生效import './assets/css/global.css'// 全局导入axiosimport axios from 'axios'// 设置axios请求的根路径axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'Vue.prototype.$http = axiosVue.config.productionTip = falsenew Vue({router,render: h => h(App)}).$mount('#app')
6 因为是按需导入的,修改element.js
import Vue from 'vue'import { Button } from 'element-ui'import { Form ,FormItem} from 'element-ui'import { Input } from 'element-ui'// 导入弹框提示组件import { Message } from 'element-ui'Vue.use(Button)Vue.use(Form)Vue.use(FormItem)Vue.use(Input)// 这里和其他组件不一样,需要通过 prototype 全局挂载 MessageVue.prototype.$message = Message
7 创建一个简单的Home.vue进行测试
<template><div><el-button type="info" @click="logout">退出</el-button></div></template><script>export default {name: "Home",methods:{logout(){window.sessionStorage.clear()this.$router.push("/login")}}}</script><style scoped></style>