项目结构 

├── dist  // 打包构建后的文件夹
├── node_modules //该项目依赖的模块
├── public  //存放静态资源(不会变动)
├── src
│   ├── assets  //存放css、图片与js文件
│   │    ├── css
│   │    ├── img
│   │    └── js
│   ├── components  //存放可复用的小组件
│   ├── http        // 封装fetch、post请求及http 拦截器配置文件
│   │    ├── api.js
│   │    └── axios.js
│   ├── router      //路由配置文件
│   │    └── index.js
│   ├── store       //vuex
│   │    └── index.js
│   ├── views       //存放主要页面(不可复用的页面组件)
│   ├── App.vue
│   └── main.js
├── .gitignore
├── babel.config.js
├── package-lock.json
├── package.json
├── README.md
└── vue.config.js

登录拦截逻辑

一:路由拦截

首先定义在路由配置文件多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,就顺利进入路由,否则就进入登录页面。

// src/router/index.js
const routes = [
    {
        path: '/home',
        name: '/home',
        component: () => import('../views/home/home.vue')
    },
    {
        path: '/user',
        name: 'user',
        meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
        },
        component: () => import('../views/user/user.vue')
    },
    {
        path: '/login',
        name: 'login',
        component: () => import('../views/login/login.vue')
    }
];

vue-router提供的钩子函数beforeEach()对路由进行判断。

//src/router/index.js
//根据我们的项目需求
router.beforeEach((to,from,next)=>{
	if(to.meta.requireAuth){  // 判断该路由是否需要登录权限
        if(localStorage.getItem("access_token")) {  // 从本地存储localStorage获取当前的token是否存在
            next()
        }else{
            next('/home') //如果token不存在,就跳到首页
        }
	}else{
        if(localStorage.getItem("access_token") && to.path == '/login') {  //token存在时候,进去登录页面就自动跳转到首页
            next('/home')
        }else{
            next()
        }
    }
});

 完整的代码,如下:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
    {
        path: '/home',
        name: '/home',
        component: () => import('../views/home/home.vue')
    },
    {
        path: '/user',
        name: 'user',
        meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
        },
        component: () => import('../views/user/user.vue')
    },
    {
        path: '/login',
        name: 'login',
        component: () => import('../views/login/login.vue')
    }
];

const router = new VueRouter({
    // mode: 'history',
    base: process.env.BASE_URL,
    routes
})

router.beforeEach((to,from,next)=>{
	if(to.meta.requireAuth){  // 判断该路由是否需要登录权限
        if(localStorage.getItem("access_token")) {  // 从本地存储localStorage获取当前的token是否存在
            next()
        }else{
            next('/home') //如果token不存在,就跳到首页
        }
	}else{
        if(localStorage.getItem("access_token") && to.path == '/login') {  //token存在时候,进去登录页面就自动跳转到首页
            next('/home')
        }else{
            next()
        }
    }
});

export default router

二:拦截器

使用axios的拦截器,统一处理所有http请求和响应。根据我们的项目需求,配合后台接口返回账号被迫下线或者返回401 Unauthorized(未授权),让用户重新登陆。

// src/http/axios.js
import axios from 'axios'
import { Modal, message } from 'ant-design-vue'
import router from '@/router'

axios.defaults.timeout = 10000 // 请求超时时间

const Service = axios.create({
    baseURL: '/',
})

// axios 请求拦截器
Service.interceptors.request.use(
    config=>{
        if(localStorage.getItem("access_token")){
			config.headers.Authorization = 'Bearer' + '  ' + localStorage.getItem('access_token');
        }
        return config
    },error=>{
        Modal.confirm({
            title: '提示',
            content: '请求超时!',
        });
        return Promise.reject(error)
    }
)

//有的一个页面请求几个接口,当token过期或者账号被迫下线,避免出现多个弹窗,自定义cont,判断cont==0时候弹窗一次,然后cont++
let cont = 0 
// axios respone拦截器
Service.interceptors.response.use(
    res=>{
        if(res.status == 200){
            if(res.data.message && res.data.message.type == 'logout'){
                if(cont == 0){
                    Modal.info({
                        title: '信息',
                        content: "你的账号在别处登陆,请注意!",
                        onOk() {
                            cont = 0
                        }
                    })
                    localStorage.removeItem('access_token');
                    localStorage.removeItem('expires_time');
                    if(router.history.current.path != '/home'){
                        router.push('/home');
                    }
                }
                cont ++;
            }

            return res;
        }else if (res.status == 401){
            router.push('/home');
            return res;
        }else if (res.status == 201) {
            return res;
        }
        return res;
    },
    error=>{
        const responseCode = error.response.status;
        switch (responseCode) {
        case 400:
            message.error('请求错误(400)')
            break
        case 401:
            if(cont == 0) {
                message.error('登录过期,请重新登录')
            }
            cont++
            llocalStorage.removeItem('access_token');
            localStorage.removeItem('expires_time');
            if(router.history.current.path != '/home'){
                router.push('/home');
            }
            break
        case 403:
            message.error('拒绝访问(403)')
            break
        case 404:
            message.error('请求出错(404)')
            break
        case 408:
            message.error('请求超时(408)')
            break
        case 500:
            message.error('服务器错误(500)')
            break
        case 501:
            message.error('服务未实现(501)')
            break
        case 502:
            message.error('网络错误(502)')
            break
        case 503:
            message.error('服务不可用(503)')
            break
        case 504:
            message.error('网络超时(504)')
            break
        case 505:
            message.error('HTTP版本不受支持(505)')
            break
        default:
            Modal.confirm({
                title: '提示',
                content: `连接出错(${error.response.status})!`,
            });
        }
        return Promise.reject(error.response.data)
    }
)

export default Service;

调用接口

// src/http/api.js
import Service from './axios.js'

// 登录
export const loginApi = data => {
    return Service({
        url: '/api/authorizations',
        method: 'post',
        data
    })
};

// 用户信息
export const userApi = data => {
    return Service({
        url: '/api/user' + data,
        method: 'get',
    })
}

登出

很简单,只需要从本地存储localStorage中当前token清除,再跳转到首页即可。

本文章根据我们的项目需求,作为个人思路笔记,仅供参考。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐