百科狗-知识改变命运!
--

laravel9 + vue3 前端使用 axios 获取携带 token 访问令牌 - vue3 项目实战

乐乐12个月前 (11-21)阅读数 16#技术干货
文章标签数据

laravel9 + vue3 前端使用 axios 获取携带 token 访问令牌

承接上文,现在开始编写 vue 前端的 axios 的请求文件。token 有三类:

  • 客户端令牌:为非登录用户获取前端数据。
  • 密码授权令牌 user:为登录用户,用户中心获取数据。
  • 密码授权令牌 admin:为后台管理员登录后,获取数据。

携带客户端令牌(token)请求数据

laravel9 + vue3 前端使用 axios 获取携带 token 访问令牌 - vue3 项目实战

客户端令牌,实现目标:当没有 token 的时候,先获得 token,然后携带 token 取请求后端数据。若不存在 token,或者 token 验证错误,无法获得数据。

向https://api.example.com/api/home/token/client发送请求,返回数据格式:{"token_type":"Bearer","expires_in":31536000,"access_token":"xxxxxx"}

文件目录:src/http/index.ts

import axios from 'axios';
import type { AxiosResponse, AxiosRequestConfig, AxiosError } from 'axios';
import { storageL } from '@/utils/webStorage';

axios.defaults.timeout = 5000;
axios.defaults.baseURL = import.meta.env.VITE_HTTP_URL;
axios.defaults.headers.common['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.withCredentials = false;

type AccessTokenCallback = (arg: string) => void;
let callbacks: AccessTokenCallback[] = [];
function token2ConfigPush(cb: AccessTokenCallback) {
  callbacks.push(cb);
}

function token2ConfigRun(token: string) {
  callbacks.map((cb) => cb(token));
}

let isLock = false;
const tokenClienUrl = '/token/client';

axios.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    const accessToken = storageL.getItem('accessToken');
    if (accessToken) {
      isLock = true;
      config.headers = {
        Authorization: 'Bearer ' + accessToken,
      };

      return config;
    } else {
      if (!isLock) {
        isLock = true;
        axios
          .get(tokenClienUrl)
          .then((response) => {
            storageL.setItem('accessToken', response.data.access_token, response.data.expires_in);
            token2ConfigRun(response.data.access_token);
            callbacks = [];
          })
          .catch((error) => {
            isLock = false;
            callbacks = [];
            storageL.removeItem('accessToken');
            return Promise.reject(error);
          });
      }

      if (config.url && config.url.indexOf(tokenClienUrl) == -1) {
        const retry = new Promise((resolve) => {
          token2ConfigPush((accessToken: string) => {
            config.headers = {
              Authorization: 'Bearer ' + accessToken,
            };
            resolve(config);
          });
        });
        return retry;
      } else {
        return config;
      }
    }
  },
  (error) => {
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  (response: AxiosResponse) => {
    return response;
  },
  (error: AxiosError) => {
    return Promise.reject(error);
  }
);

export default axios;

文件目录src//utils/webStorage

type storageValue = string | number | boolean | null | object | object[] | string[] | number[] | boolean[] | null[];

interface storageItem {
  value: storageValue;
  expires: number;
}

class webStorage {
  public memory: Storage;
  constructor(type: string) {
    if (type === 'localStorage') {
      this.memory = window.localStorage;
    } else {
      this.memory = window.sessionStorage;
    }
  }

  //小时转化为秒
  public hour2Second(hour: number) {
    return hour * 60 * 60;
  }

  //当设置了有效期,时间为秒s。默认值0,长期有效。
  public setItem(key: string, value: storageValue, second = 0) {
    const source: storageItem = { value: null, expires: 0 };
    if (second) {
      source.expires = new Date().getTime() + second;
    }
    source.value = value;
    const data = JSON.stringify(source);
    this.memory.setItem(key, data);
  }

  //当有效期为0时,永久有效。否则检测是否过期,过期则清除。
  public getItem(key: string) {
    const data = this.memory.getItem(key);
    if (data) {
      if (data.length == 0) return '';
      const source: storageItem = JSON.parse(data);
      const now = new Date().getTime();
      if (source.expires && now > source.expires) {
        this.memory.removeItem(key);
        return '';
      }
      return source.value;
    } else {
      return '';
    }
  }

  public removeItem(key: string) {
    this.memory.removeItem(key);
  }

  public clear() {
    this.memory.clear();
  }
}

const storageL = new webStorage('localStorage');
const storageS = new webStorage('sessionStorage');
export { storageL, storageS };


Javascript数组中存储函数

//定义储存函数的数组
var funs=[
   function(){ echo "1"; },
   function(){ echo "2"; },
   function(){ echo "3"; },
   function(){ echo "3"; },
];

//调用执行
funs[1]();
//定义储存函数的对象
var functions = {
    blah: function(){ alert("blah"); },
    foo: function() { console.log("foo"); }
};

//调用执行
functions.blah();
functions["blah"]();


携带密码授权令牌(token)请求数据

管理后台,管理员 admin 登录后,获得密码授权令牌(token),每次请求都需要携带密码授权令牌(token)。若不存在 token,或者 token 验证错误。强制退出登录。

文件目录:src/http/adminHttp.ts

import axios from 'axios';
import type { AxiosResponse,InternalAxiosRequestConfig, AxiosError } from 'axios';
import { useAdminTokenStore } from '@/stores';

axios.defaults.timeout = 5000;
axios.defaults.baseURL = import.meta.env.VITE_HTTP_URL;
axios.defaults.headers.common['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.withCredentials = false;

axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    if (config.url && config.url.includes(import.meta.env.VITE_TOKEN_LOGIN)) {
      return config;
    }
    const adminTokenStore = useAdminTokenStore();
    const adminToken = adminTokenStore.getToken;
    if (adminToken) {
      if (config.headers && !config.headers['Authorization']) {
        config.headers['Authorization'] = 'Bearer ' + adminToken;
      }
      return config;
    } else {
      adminTokenStore.removeToken();
      return Promise.reject();
    }
  },
  (error) => {
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  (response: AxiosResponse) => {
    return response;
  },
  (error: AxiosError) => {
    console.log(error.config);
    return Promise.reject(error);
  }
);

export default axios;

⚠️注意:新版axious 1.2+,类型是InternalAxiosRequestConfig,旧版是AxiosRequestConfig。


Pinia:@/stores/index

import { defineStore } from 'pinia';

interface User {
  email: string;
  nickname: string;
  icon: string;
  role: string;
}

export const useAdminStore = defineStore({ id: 'admin', state: () => ({
    user: {
      email: '',
      nickname: '',
      icon: '',
      role: '',
    } as User,
  }), getters: {
    getUser(): User {
      const storage = new webStorage('localStorage');
      this.user = storage.getItem('admin') as User;
      return this.user;
    },
  }, actions: {
    setUser(data: User, expires_in: number) {
      this.user = data;
      const storage = new webStorage('localStorage');
      storage.setItem('admin', data, expires_in);
    },
    removeUser() {
      this.user = { email: '', nickname: '', icon: '', role: '' };
      const storage = new webStorage('localStorage');
      storage.removeItem('admin');
    },
  },
});



cookie、localStorage、sessionStorage

cookie

Cookie基于HTTP规范,Cookie诞生之初的作用就是解决HTTP的无状态请求,用来记录一些用户相关的一些状态。Cookie是服务器发送到浏览器的一小段数据,通常式用来辨别用户身份的数据,会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。

cookie 格式
document.cookie="my_cookie_name=my_cookie_value;expires=toDate;domain=www.baidu.com;path=/"
  • “my_cookie_name=my_cookie_value”这一字段指定了cookie名“my_cookie_name”和cookie值“my_cookie_value”。
  • expires 指定 cookie 过期时间是“toDate”,注意“toDate”必须是UTC时区的时间字符串(可以通过Date.prototype.toUTCString()转换得到)。
  • domain 指定该 cookie 支持的域。比如:www.baidu.com。
  • path 指定 cookie 的有效路径。指定为‘/’时表示所有路径有效,如果指定为‘/api’则表示仅在‘/api’下的所有路径有效。
设置 cookie
function setCookie(name, value, hour, domain, path)
{
    var exp = new Date()
    exp.setTime(exp.getTime() + (hour*60*60*1000))
    var expires = "expires=" + exp.toUTCString()
    var path_ = path ? "path=" + path : "path=/"
    var domain_ = domain ? "domain=" + domain + ';' : ';'
    document.cookie = name + "=" + escape (value) + ";" + domain_ + expires + ";" + path_
}

获得 cookie
function getCookie(name)
{
    const reg = new RegExp('(^| )'+ name + '=([^;]*)(;|$)')
    if (arr = document.cookie.match(reg)) {
        return unescape(arr[2])
    } else {
        return ''
    }
}

删除 cookie
function clearCookie(name)
{
    setCookie(name, '', -1)
}
  • escape(string):函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。
  • unescape(string):函数可对通过 escape()编码的字符串进行解码。


webStorage

webStorage 基于 HTML5 规范。HTML5 提供了两种在客户端存储数据的新方法:localStoragesessionStorage,挂载在window对象下。webStorage 是本地存储,数据不是由服务器请求传递的。从而它可以存储大量的数据,而不影响网站的性能。webStorage的目的是为了克服由cookie带来的一些限制,当数据需要被严格控制在客户端上时,无须持续地将数据发回服务器。比如客户端需要保存的一些用户行为或数据,或从接口获取的一些短期内不会更新的数据,我们就可以利用webStorage来存储。

  • localStorage生命周期是永久性的。localStorage存储的数据,即使关闭浏览器,也不会让数据消失,除非主动的去删除数据。localStorage 被大多数浏览器所支持,IE8+。
  • sessionStorage数据只在会话期间有效,刷新页面数据依旧存在。但当页面关闭后,数据就会被清空。sessionStorage 被大多数浏览器所支持,IE8+。

localStorage 和 sessionStorage 所使用的方法是一样的,下面以sessionStorage为例子:

var name='sessionData';
var num=120;

sessionStorage.setItem(name,num);   //存储数据
sessionStorage.setItem('value2',119);

let dataAll=sessionStorage.valueOf();   //获取全部数据
console.log(dataAll,'获取全部数据');

var dataSession=sessionStorage.getItem(name);   //获取指定键名数据
var dataSession2=sessionStorage.sessionData;   //sessionStorage是js对象,也可以使用key的方式来获取值
console.log(dataSession,dataSession2,'获取指定键名数据');

sessionStorage.removeItem(name);   //删除指定键名数据
console.log(dataAll,'获取全部数据1');

sessionStorage.clear();   //清空缓存数据:localStorage.clear();
console.log(dataAll,'获取全部数据2'); 


三者异同

特性CookielocalStoragesessionStorage
数据的生命期可设置失效时间,默认是关闭浏览器后失效除非被清除,否则永久保存仅在当前会话下有效,关闭页面或浏览器后被清除
存放数据大小4K左右一般为 5MB一般为 5MB
与服务器端通信每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题仅在客户端(即浏览器)中保存,不参与和服务器的通信仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性需要程序员自己封装,源生的Cookie接口不友好源生接口可以接受,亦可再次封装来对Object和Array有更好的支持源生接口可以接受,亦可再次封装来对Object和Array有更好的支持


vue储存数据

在 vue 中储存数据,可以使用本地储存 localstorage 、sessionStorage,还可以储存在 vuex 中。

  • vuex 中的数据具备响应性,会随着数据的改变而渲染视图,另外 vuex 是储存在内存中的,读取速度快,但是一旦刷新浏览器,数据就会丢失。
  • localstorage 、sessionStorage的数据不是响应式的,即使本地存储数据改变了,视图也不会立刻渲染,另外其数据是储存在硬盘上的,读取速度相对vue,要慢很多。但能长期有效。

所以,在实际开发中,把像 token 这样的数据会分别储存在本地数据中,还会出现在 vuex 中,共储存两份。首先读取 vuex 中数据,若不存在,再读取 localstorage(或 sessionStorage)中数据。每次更新token也是修改vuex中的token,然后再覆盖到localstorage 中。

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)