1.前端将用户名和密码调用接口传给后端。 2.后端收到请求,验证用户名和密码是否正确,验证成功,返回一个token。 3.前端拿到token后,存储到localStorage和vuex中,并进行页面跳转。 4.前端每次跳转路由,都要判断localStroage有无token,没有则跳转登录页,有则跳转至对应路由页。 5.每次调用后端接口,都要在请求头中携带token。 6.后端判断请求头中有无token并验证,验证成功则返回数据,验证失败或没有token则返回401。 7.如果前端拿到401的状态码,则清空token信息并跳转登录页。
npm install vuex // 安装vuex
建立store
文件夹,创建index.js
文件此时vuex里面主要存储token的相关信息,代码如下:
import { createStore } from "vuex";
// 用Vuex.Store对象用来记录token
const store = createStore({
state: {
// 存储token
token:"",
userName:"" ,// 可选
passWord:""
},
getters: {
getToken(state){
return state.token || localStorage.getItem("token") || "";
}
},
mutations: {
// 修改token,并将token存入localStorage
setToken(state,token) {
state.token = token;
console.log('state.token: ', state.token);
localStorage.setItem('token', token);
console.log('store、localstorage保存token成功!');
},
delToken(state) {
state.token = "";
localStorage.removeItem("token");
},
// 可选
setUserInfo(state, userName) {
state.userName = userName;
}
},
actions: {
// removeToken: (context) => {
// context.commit('setToken')
// }
},
});
export default store;
在main.js中引用
import store from './store'
npm install axios; // 安装axios
创建network
文件夹,再创建request.js
文件.
在封装axios时,使用QS插件,增加一些安全性的查询字符串解析和序列化字符串的库。
安装npm install qs,在request.js文件中引用,import qs from 'qs';
/*
* @LastEditors: zhouyanhong
* @Description: ...
* @Date: 2023-01-14 16:28:07
* @LastEditTime: 2023-01-15 10:10:30
* @Author: zhouyanhong
*/
import qs from 'qs'
import axios from 'axios'
import store from '../store/index'
import { ElMessage } from 'element-plus'
import router from '../router/index'
// 使用自定义的配置文件发送请求
const instance = axios.create({
baseURL: '',
timeout: 80000,
headers: {
// 'Content-Type': 'application/json;charset=UTF-8',
// 'custome-header':'tianliangjiaoyu'
}
})
// post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置,即设置post的请求头为
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 每次发送请求之前判断vuex中是否存在token
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.getters.getToken;
console.log('token: ', token);
if (token) {
// 已经登录成功,统一添加token
config.headers.Authorization = `Bearer ${token}`
}
// token && (config.headers.Authorization = token);
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
})
instance.interceptors.response.use(function (response) {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
}, function (error) {
// 对响应错误做点什么
if (error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
break;
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
ElMessage({
message: '登录过期,请重新登录',
duration: 1000,
type: 'success'
});
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
// 404请求不存在
case 404:
ElMessage({
message: '网络请求不存在',
duration: 1500,
type: 'success'
});
break;
// 其他错误,直接抛出错误提示
default:
ElMessage({
message: error.response.data.message,
duration: 1500,
type: 'success'
});
}
return Promise.reject(error.response);
}
});
//封装对应的方法
const $get = (url, params) => {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params,
})
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
const $post = (url, params) => {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params)) //是将对象 序列化成URL的形式,以&进行拼接
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
//下面是vue3必须加的,vue2不需要,只需要暴露出去get,post方法就可以
export default {
install: (app) => {
app.config.globalProperties['$get'] = $get;
app.config.globalProperties['$post'] = $post;
app.config.globalProperties['$axios'] = axios;
}
}
此时需要理解一下如何封装axios, 1.添加请求拦截器,发送请求之前判断是否存在token,如果存在统一在http的请求中加上token 2.添加响应拦截器,如果响应成功,调用promise.resolve(),响应失败根据不同的状态码做出不同的处理结果。401:未登录,没权限访问。403:登录过期。404:请求不存在。 3.封装get,post,put,delete等请求方法
重难点:实现动态添加路由 公共的路由,每个用户都可以访问的
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(), // hash模式:createWebHashHistory,history模式:createWebHistory
routes: [
{
path: "/",
name: "Home",
component: () => import("@/views/Home"),
},
{
path: "/home",
name: "Home",
component: () => import("@/views/Home"),
},
{
path: "/404",
name: "404",
component: () => import("@/views/404"),
},
{
path: "/notAllow",
name: "notAllow",
component: () => import("@/views/notAllow"),
},
{
path: "/login",
name: "Login",
component: () => import("@/views/Login"),
meta: {
noLogin: true, //无须登录即可浏览
},
},
]
})
export default router
登录进去时有一些路由是所有人可见,设置为默认路由。
在进行路由跳转时,设置路由守卫,在进页面之前,判断有token,才进入页面,否则返回登录页面。
通过asyncRoutestMark
判断路由是否有过拼接,然后循环navigationList
(模拟接口返回数据),通过router.addRoute
向数据组添加数据,通过router.getRoutes()
存储路由,并放在vuex中。
router.beforeEach((to, from, next) => {
const isLogin = localStorage.token ? true : false
console.log('isLogin: ', isLogin);
console.log("navigationList", navigationList)
console.log("11111111111111", store.state.asyncRoutestMark)
if (isLogin) {
if (!store.state.asyncRoutestMark) {
navigationList.forEach(navigation => {
router.addRoute('home', {
path: navigation.url,
meta: { name: navigation.name, isAsync: true, icon: navigation.icon },
name: navigation.url,
component: () => import(`../views/${navigation.url}`)
})
})
console.log(router.getRoutes(), '查看现有路由')
console.log("to",to)
store.commit('changeRouter', true)
store.commit("setAllRoutes",router.getRoutes())
next({ ...to, replace: true })
}else {
next()
}
}else {
next()
}
})