• Vue如何实现登录功能?
  • 发布于 2个月前
  • 196 热度
    0 评论
实现登陆的思路:
1.前端将用户名和密码调用接口传给后端。
2.后端收到请求,验证用户名和密码是否正确,验证成功,返回一个token。
3.前端拿到token后,存储到localStorage和vuex中,并进行页面跳转。
4.前端每次跳转路由,都要判断localStroage有无token,没有则跳转登录页,有则跳转至对应路由页。
5.每次调用后端接口,都要在请求头中携带token。
6.后端判断请求头中有无token并验证,验证成功则返回数据,验证失败或没有token则返回401。

7.如果前端拿到401的状态码,则清空token信息并跳转登录页。


具体实现过程

1.安装vuex
npm install vuex  // 安装vuex
2.建立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: {
    // 堆代码 duidaima.com
    // 修改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;
3.在main.js中引用
import store from './store'
4.封装axios
npm install axios; // 安装axios
5.创建network文件夹,再创建request.js文件.
6.在封装axios时,使用QS插件,增加一些安全性的查询字符串解析和序列化字符串的库。
7.安装npm install qs,在request.js文件中引用,import qs from 'qs';
/*
 * @堆代码 duidaima.com
 * @Date: 2023-10-14 16:28:07
 */
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等请求方法


封装vue-router
重难点:实现动态添加路由
公共的路由,每个用户都可以访问的
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()
  }
})
用户评论