vueプロジェクトは、トークンを(vuex)ストアとlocalstorageに保存します


注意深く読んで、何度も何度も考えてください、Xiaobaiが始まります!ご不明な点がございましたら、メッセージを残してください❤

1.準備とトークン

1.準備
  • (セッション、Cookie)トークン
    トークンの導入を理解する:トークンはクライアント側のサーバーにデータを頻繁に要求し、サーバーはデータベースにユーザー名とパスワードを頻繁に照会し、それらを比較してユーザー名とパスワードが正しいかどうかを判断し、対応するプロンプトを表示します。この文脈で、トークンが生まれました。
    トークンは、クライアントが要求するトークンとして機能するためにサーバー上で生成された文字列の文字列です。フロントエンドがユーザー名/パスワードを使用してサーバーに認証を要求し、サーバー認証が成功した場合、サーバーはトークンをフロントエンドに返します。フロントエンドは、要求するたびにトークンを持ってその法的ステータスを証明することができます。このトークンがサーバー上で永続的である場合(データベースに保存されている場合など)、永続的なIDトークンです(有効期間が設定されていない場合)。


  • トークンの利点トークンはアプリケーションによって完全に管理されるため、同じ発信元のポリシー
    を回避
    できますトークンはCSRF攻撃を回避できますトークンはステートレスであり、複数のサービス間で共有できる
    ため、サーバーへの負荷を軽減し、頻繁なデータベースクエリを削減し、サーバーをより堅牢にします。 。

  • vuexをインストールします
    cnpm install vuex --save

2.トークンの使用法の概要

フロントエンドとバックエンドが完全に分離されている場合、Vueプロジェクトでトークン検証を実装する一般的な考え方は次のとおりです:

1.初めてログインするとき、フロントエンドはバックエンドのログインインターフェイスを調整し、ユーザー名とパスワードを送信します

2.バックエンドはリクエストを受信し、ユーザー名とパスワードを検証し、検証が成功すると、フロントエンドにトークンを返します。

3.フロントエンドはトークンを取得し、トークンをlocalStorageとvuexに保存して、ルーティングページにジャンプします。

4.フロントエンドがルートにジャンプするたびに、localStroageにトークンがあるかどうかを判断し、トークンがない場合はログインページにジャンプし、トークンがある場合は対応するルーティングページにジャンプします。

5.バックエンドインターフェイスを調整するたびに、トークンをリクエストヘッダーに追加する必要があります

6.バックエンドは、リクエストヘッダーにトークンがあるかどうかを判断します。トークンがある場合は、トークンを取得して検証します。検証が成功した場合は、データを返します。検証が失敗した場合(たとえば、トークンの有効期限が切れた場合)は、401を返します。リクエストヘッダーにトークンがない場合は、401も返します。

7.フロントエンドがステータスコード401を取得した場合は、トークン情報をクリアしてログインページにジャンプします
————————————————
著作権ステートメント:この記事は、CSDNブロガー「sleepwalker_1992」のオリジナル作品です。この記事はCC4.0 BY-SA著作権契約に準拠しています。再印刷するには、元のソースリンクとこのステートメントを添付してください。
元のリンク:https://blog.csdn.net/sleepwalker_1992/article/details/82974703


2.ストレージの作成、保存、リクエスト

1. srcディレクトリ:

ここに写真の説明を挿入
注:localstorage(キャッシング)をstorage.jsにカプセル化したため、ストレージの作成はオプションです(この記事の残りの部分では、カプセル化したjsを使用します)。ストアの作成が必要です

2.ストレージを作成します(オプション)
// 封装操作localstorage本地存储的方法  模块化

var storage = {
    
    
    set(key, value) {
    
    
        localStorage.setItem(key, JSON.stringify(value));
        // localStorage.key = value;
        // localStorage[key] = value;
    },
    get(key) {
    
    
        return JSON.parse(localStorage.getItem(key));
    },
    getForIndex(index) {
    
    
        return localStorage.key(index);
    },
    getKeys(){
    
    
        let items = this.getAll();
        let keys = [];
        for (let index=0;index<items.length;index++){
    
    
            keys.push(items[index].key);
        }
        return keys;
    },
    getLength() {
    
    
        return localStorage.length;
    },
    getSupport() {
    
    
        return (typeof (Storage) !== "undefined") ? true : false;
    },
    remove(key) {
    
    
        localStorage.removeItem(key);
    },
    removeAll() {
    
    
        localStorage.clear();
    },
    getAll() {
    
    
        let len = localStorage.length;  // 获取长度
        let arr = new Array(); // 定义数据集
        for (var i = 0; i < len; i++) {
    
    
            // 获取key 索引从0开始
            var getKey = localStorage.key(i);
            // 获取key对应的值
            var getVal = localStorage.getItem(getKey);
            // 放进数组
            arr[i] = {
    
    
                'key': getKey,
                'val': getVal,
            }
        }
        return arr;
    }
}

export default storage;
3.ストアを作成します

import Vue from 'vue'
import Vuex from 'vuex'
import storage from '@/model/storage'

Vue.use(Vuex);
// 用Vuex.Store对象用来记录token
const store = new Vuex.Store({
    
    

  state: {
    
    
    // 存储token
    // token: storage.get('token') ? storage.get('token') : '',
    token:"",
    userName:"" // 可选
  },

  actions: {
    
    
    // removeToken: () => {
    
    
      // context.commit('set_token')
    // }
  },

  // 计算属性
  mutations: {
    
    
    // 修改token,并将token存入localStorage
    set_token(state,token) {
    
    
      state.token = token;
      storage.set('token', token);
      console.log('store、localstorage保存token成功!');
    },
    del_token(state) {
    
    
      state.token = "";
      storage.remove("token");
    },
    // 可选
    setUserInfo(state, userName) {
    
    
      state.userName = userName;
    }
  }
});

export default store;
4.リクエストを作成します
import axios from 'axios'
import store from '@/store'
import router from '@/router'

// create an axios instance
const service = axios.create({
    
    
    // index.js设置了代理(解决跨域) invoice = http://58.246.79.142:25005
    baseURL: "/invoice", // url = base url + request url
    timeout: 5000, // request timeout
   }

})

//添加请求拦截器,若token存在则在请求头中加token,不存在也继续请求
service.interceptors.request.use(
    config => {
    
    
        // 每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器,没有则不带token
        // Authorization是必须的
        if (store.state.token) {
    
    
            config.headers.Authorization = store.state.token;
        }
        return config;
    },
    error => {
    
    
        console.log("在request拦截器显示错误:", error.response)
        return Promise.reject(error);
    }
);

//respone拦截器
service.interceptors.response.use(
    response => {
    
    
        // 在status正确的情况下,code不正确则返回对应的错误信息(后台自定义为200是正确,并且将错误信息写在message),正确则返回响应
        return response.data.code == 200 ? response : Promise.reject(response.data.message);
    },
    error => {
    
     
        // 在status不正确的情况下,判别status状态码给出对应响应
        if (error.response) {
    
    
            console.log("在respone拦截器显示错误:", error.response)
            switch (error.response.status) {
    
    
                case 401:
                    //可能是token过期,清除它
                    // this.$store.commit("del_token"); 
                    store.commit("del_token");

                    router.replace({
    
     //跳转到登录页面
                        path: '/login',
                         // 将跳转的路由path作为参数,登录成功后跳转到该路由
                        query: {
    
     redirect: router.currentRoute.fullPath }
                    });
            }
        }
        return Promise.reject(error.response.data);
    }
);


export default service

3.プロキシを構成し、ルーターをカプセル化し、ルーターガードを設定し、ルーターをインポートしてmain.jsに保存します。

1つは、プロキシを構成する

ここに写真の説明を挿入

2.ルーティングルーターをカプセル化し、ルーティングガードを設定します
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/main/index'

import store from '@/store'
import storage from '@/model/storage'

Vue.use(Router)

const routes = [
  {
    
    
    path: '/',
    name: 'Main',
    redirect: '/login',
    // 某些页面规定必须登录后才能查看 ,可以在router中配置meta,将需要登录的requireAuth设为true,
    meta: {
    
    
      requireAuth: true,
    }
  },

  {
    
    
    path: '/login',
    component: () => import('@/views/login'),
  },

  {
    
    
    path: '/invoice',
    redirect: '/invoice',
    component: Main,
  },

    ]
  },
  {
    
    
    path: '*',
    component: Main
  }
]

const router = new Router({
    
     routes: routes })

// 设置路由守卫,在进页面之前,判断有token,才进入页面,否则返回登录页面
if (storage.get("token")) {
    
    
  store.commit("set_token", storage.get("token"));
}
router.beforeEach((to, from, next) => {
    
    
  // 判断要去的路由有没有requiresAuth
  // to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
  if (to.matched.some(r => r.meta.requireAuth)) {
    
    
    if (store.state.token) {
    
    
      next(); //有token,进行request请求,后台还会验证token
    } else {
    
    
      next({
    
    
        path: "/login",
        // 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
        query: {
    
     redirect: to.fullPath }  
      });
    }
  } else {
    
    
    next(); //如果无需token,那么随它去吧
  }
});

export default router
3.ルーターを導入し、main.jsに保存します
import Vue from 'vue'
import App from './App'
import router from './router'

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'
import store from '@/store'

// 兼容ie
// import 'babel-polyfill';

Vue.use(ElementUI);
Vue.config.productionTip = false
// 定义vue属性,就可以全局使用this.$store
Vue.prototype.$store = store;

new Vue({
    
    
  el: '#app',
  router:router,
  components: {
    
     App },
  template: '<App/>'
})

第四に、ログインページの実際の使用

フォーム送信の例の関数と依存関係jsのみを提供します

import {
    
     postLogin } from "@/api/login";

submitForm(formName) {
    
    
      this.$refs[formName].validate(valid => {
    
    
        if (valid) {
    
    
          let that = this;
          // console.log('username',this.loginForm.username)
          // 通过校验规则后进入校验用户名密码是否正确
          // 没有后台接口可以自定义一个函数来模拟,替换postLogin
          postLogin(this.loginForm.username, this.loginForm.password)
            .then(res => {
    
    
              console.log(res);
              
               that.$store.commit("set_token", res.data.token);
               that.$store.commit("setUserInfo", res.data.account);

              this.$notify({
    
    
                title: "登录成功",
                type: "success",
                showClose: false,
                duration: 1000
              });
              setTimeout(() => {
    
    
                // 此时要判断/login后面的参数,若无参数,进入主页;
                this.$router.push("/index");
                // 若有参数则参数为未有权限的那个路由,跳转到那个路由
                // this.$router.push(***); -- 具体要自己在这实现
              }, 1000);
            })
            .catch(error => {
    
    
            // 错误分为 status-请求错误 和 code-账号密码错误
              this.$message.error(error);
              console.log(error);
            });
        } else {
    
    
        // 不符合前端校验
          this.$message.error('format error:'+error);
          console.log('format error:',error);
          return false;
        }
      });
    }

上記が依存する@ / Api / login:

import request from '@/utils/request.js'  // 前面封装的request
// 在request中,设置了根路径/invoice,所以每一个请求都会自动在前面带上invoice;
// 由于在config中的index配置过了代理(监听)的路径也是/invoice,但凡遇到/invoice路径就会替换为target目标地址(后台接口根地址)
// 实现跨域,即不限于只能访问根路径为127.0.0.1下的地址
export function postLogin(account,password) {
    
    
    console.log(account,password)
    return request({
    
    
        url: '/login',
        method: 'post',
        params:{
    
    
        // 具体传参(键)要看后台要求
            account:account,
            password:password
        }
    })
}

おすすめ

転載: blog.csdn.net/GeniusXYT/article/details/104402757