vue 프로젝트는 (vuex) 저장소 및 로컬 저장소에 토큰을 저장합니다.


주의 깊게 읽고 계속해서 생각하면 Xiaobai가 시작됩니다! 질문이 있으시면 메시지를 남겨주세요 ❤

1. 준비 및 토큰

1. 준비
  • (세션, 쿠키) 토큰
    도입 이해 토큰 : 토큰은 클라이언트 측의 서버에 데이터를 자주 요청하고 서버는 사용자 이름과 암호를 자주 쿼리하고 비교하여 사용자 이름과 암호가 올바른지 여부를 확인하고 해당 프롬프트를 만듭니다. 이러한 맥락에서 Token이 탄생했습니다.
    토큰은 클라이언트가 요청할 토큰 역할을하기 위해 서버에서 생성 된 문자열 문자열입니다. 프런트 엔드가 사용자 이름 / 암호를 사용하여 서버에 인증을 요청하고 서버 인증이 성공하면 서버는 토큰을 프런트 엔드로 반환합니다. 프런트 엔드는 토큰을 요청할 때마다 법적 상태를 증명할 수 있습니다. 이 토큰이 서버에 영구적 인 경우 (예 : 데이터베이스에 저장 됨) 영구 ID 토큰입니다 (유효 기간이 설정되지 않은 경우).


  • 토큰의 장점 토큰은 애플리케이션에 의해 완전히 관리되므로 동일 출처 정책
    을 피할
    수 있습니다 . 토큰은 CSRF 공격을 피할 수 있습니다 . 토큰은 상태 비 저장이 가능하고 여러 서비스간에 공유 할 수있어
    서버에 대한 부담을 줄이고 빈번한 데이터베이스 쿼리를 줄이며 서버를 더욱 강력하게 만들 수 있습니다. .

  • vuex 설치
    cnpm 설치 vuex --save

2. 토큰 사용 소개

프런트 엔드와 백 엔드가 완전히 분리 된 경우 Vue 프로젝트에서 토큰 검증을 구현하는 일반적인 아이디어는 다음과 같습니다.

1. 처음 로그인 할 때 프런트 엔드는 백 엔드의 로그인 인터페이스를 조정하고 사용자 이름과 암호를 보냅니다.

2. 백엔드는 요청을 수신하고 사용자 이름과 암호를 확인하고 확인이 성공하면 프런트 엔드에 토큰을 반환합니다.

3. 프런트 엔드는 토큰을 가져 와서 localStorage 및 vuex에 토큰을 저장하고 라우팅 페이지로 이동합니다.

4. 프론트 엔드가 경로로 점프 할 때마다 localStroage에 토큰이 있는지 판단하고 토큰이 없으면 로그인 페이지로 점프하여 해당 라우팅 페이지로 점프합니다.

5. 백엔드 인터페이스가 조정될 때마다 요청 헤더에 토큰을 추가해야합니다.

6. 백엔드는 요청 헤더에 토큰이 있는지 여부를 판단합니다. 토큰이 있으면 토큰을 가져 와서 토큰을 확인합니다. 확인이 성공하면 데이터를 반환합니다. 확인에 실패하면 (예 : 토큰 만료) 401을 반환합니다. 요청 헤더에 토큰이 없으면 401도 반환합니다.

7. 프런트 엔드가 401 상태 코드를 받으면 토큰 정보를 지우고 로그인 페이지로 이동합니다.
————————————————
저작권 설명 :이 기사는 CSDN 블로거 "sleepwalker_1992"의 원본입니다. 기사는 CC 4.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에 저장합니다.

하나, 프록시 구성

여기에 사진 설명 삽입

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