登录业务流程:在登录页面输入用户名和密码,调用后台接口进行验证,通过验证之后,根据后台响应的状态跳转到项目主页
登录业务的相关技术点:
- 1.http是无状态的
- 2.通过cookie在客户端记录状态,通过session在服务端记录状态(用户用账号和密码登录成功以后,后端php,java等,就会返回给一个 sessionId的值,存在数据库redis里面, 然后再通过一些 set-cookie响应头 发送给前端, 浏览器就会自动将这个值保存在用户电脑上面,称为cookie; 然后我们所有的请求发送之前 都会自动带上cookie这个参数放在请求头里面; 因为以前的haulcookie因为不能跨域,所以现在使用比较流行的token方式)
- 3.通过token方式维持状态
token原理:用户在客户端登录页面输入用户名和密码进行登录,服务器验证通过之后生成该用户的token并返回,客户端收到服务器返回的token将它存储起来,后续客户端的所有请求都携带该token发送请求,服务器验证token是否通过。
登录的背后:
login页面的逻辑demo:
<script>
import asyncErrorHandler from '@/utils/asyncErrorHandler'
import { login } from '@/users/user'
import '@/store/index'
export default {
name: 'login',
data() {
return {
form: {
name: '13811111111',
password: '246810'
},
rules: {
name: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 15, message: '用户名格式不正确,请重新输入', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 6, message: '密码格式不正确,请重新输入', trigger: 'blur' }
]
}
}
},
methods: {
async LoginSubmitForm() {
this.$refs.LoginForm.validate((a, b) => {
// console.log(a, b)
if (!a) return this.$message.error('格式不正确,请重新输入!')
})
// 发送登录请求
const [err, res] = await asyncErrorHandler(
login({
mobile: this.name,
code: this.password
})
)
this.$refs.LoginForm.resetFields()
// 存储token在vuex
this.$store.commit('setToken', res.data)
// 跳转
this.$router.push('/home')
}
}
}
</script>
import asyncErrorHandler from '@/utils/asyncErrorHandler'
里面的asyncErrorHandler错误处理:
// 这个函数专门来处理 程序里大量的try...catch错误处理
// 在写返回值的时候,要按照node.js里面的error first原理,错误对象有限的手段,把错误对象放在第一位
export default function(promise) {
return promise
.then((res) => {
return [null, res]
})
.catch((err) => {
if (err.response && err.response.status === 400) {
this.$message({
duration: 1000,
message: 'xxx错误',
type: 'error'
})
return [err, null]
} else if (err.response && err.response.status === 404) {
this.$message({
duration: 1000,
message: 'xxx错误',
type: 'error'
})
return [err, null]
}
})
}
import { login } from '@/users/user'
里面的login:
import request from '@/utils/request'
import config from '@/config/url.config'
// 登录请求
export const login = function(data) {
return request({
url: config.login,
method: 'post',
data
})
}
export default { login }
import config from '@/config/url.config'
里面的代码:
export default {
token: 'USER_TOKEN',
baseURL: 'http://ttapi.research.itcast.cn/app/',
// 登录
login: 'v1_0/authorizations'
// 还可以写接口.....
}
import request from '@/utils/request'
的代码:
import axios from 'axios'
import config from '@/config/url.config'
import { getItem, setItem } from '@/utils/storage'
const request = axios.create({
baseURL: config.baseURL
})
// 路由白名单
const whiteList = ['login']
// 添加请求拦截器
request.interceptors.request.use(
function(conf) {
// 在发送请求之前做些什么
// 判断用户是否登录并且有token值,有的话发请求携带上
if (!whiteList.includes(conf.rul) && getItem(config.token)) {
// 携带请求头 本地存储键值对的对象,而这里需要拼接token只
conf.headers.Authorization = `${getItem(config.token).token}`
}
return conf
},
function(error) {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 添加响应拦截器
request.interceptors.response.use(
function(response) {
// 对响应数据做点什么
return response
},
function(error) {
// 对响应错误做点什么
return Promise.reject(error)
}
)
export default request
import '@/store/index'
里面的代码:
import Vue from 'vue'
import Vuex from 'vuex'
import { getItem, setItem } from '../utils/storage'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: getItem()
},
mutations: {
setToken(state, payload) {
state.token = payload
setItem(payload)
}
},
actions: {},
modules: {}
})
import { getItem, setItem } from '../utils/storage'
的代码:
// 本地持久化token
import config from '@/config/url.config'
export const setItem = function(data) {
let tmp = ''
if (typeof data === 'object') {
tmp = JSON.stringify(data)
}
window.localStorage.setItem(config.token, tmp)
}
export const getItem = function() {
const tmp = window.localStorage.getItem(config.token)
? window.localStorage.getItem(config.token)
: null
return tmp ? JSON.parse(tmp) : null
}
路由守卫代码:
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入 getItem 持久化方法
import { getItem } from '@/utils/storage'
// 导入config 获取本地存储的名称
import config from '../config/url.config'
Vue.use(VueRouter)
const routes = [
// 定义路由规则
{ path: '/', redirect: '/login' }, // 重定向
{
path: '/login',
name: 'Login',
component: () => import('@/views/login.vue')
},
{
path: '/home',
name: 'Home',
component: () => import('../views/home')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 设置路由守卫,用户必须登录才可以
router.beforeEach((to, from, next) => {
// 每次跳转路由之前,都需要判断有没有登录,如果没有登录强制跳转到登录页面
const tokenStr = getItem(config.token)
// 如果是从登陆页面跳转过来的,直接放行,因为用户已经登录过了
if (to.path === '/login') return next()
// 如果本地无法获取token值,那么用户不能通过直接输入路由地址进行跳转,强行让用户进入登录界面
if (!tokenStr) return next('/login')
// 如果用户已经登录 代表有权限可以自由跳转所有的路由
next()
})
export default router
登录背后的总结:
- 请求可以拆分成模块化来写,1.把错误进行捕获 提示用户(错误处理封装);2.基础url、接口进行封装到一个文件(url.config.js)3.所有的请求也封装起来写(user.js)
- 导航守卫,有些页面只有登录了才有权限去访问,有些页面不需要登录就可以访问,不需要登录就可以访问的就是路由白名单
- 登录请求成功之后,把token在vuex进行数据储存,以及本地数据持久化(utils/storage.js)
- 在请求拦截器里把需要权限才能访问的页面,添加token属性到Authrization字段,在拦截器里进行所需要校验的页面,完成筛选校验(utils/request.js)
- 登录成功以后,根据服务器返回的数据,动态添加路由;不同角色有不同的权限,不同角色登录进去,返回的数据和看到的页面是不一样的