自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Vue 3 中如何對(duì) JWT、Vuex、Axios和Vue Router 進(jìn)行身份驗(yàn)證實(shí)戰(zhàn)

開(kāi)發(fā) 前端
App?組件是一個(gè)具有Router?的容器。它從Vuex store/auth?獲取應(yīng)用狀態(tài)。然后導(dǎo)航欄可以根據(jù)狀態(tài)來(lái)顯示。App組件還會(huì)將狀態(tài)傳遞給子組件。

在本教程中,我們將在Vue 3中使用JWT、Vuex、Axios、Vue Router和VeeValidate構(gòu)建一個(gè)身份驗(yàn)證和授權(quán)的示例。

內(nèi)容包括:

  • 用戶(hù)注冊(cè)和用戶(hù)登錄的JWT身份驗(yàn)證流程
  • 使用Vuex 4和Vue Router 4進(jìn)行Vue 3身份驗(yàn)證的項(xiàng)目結(jié)構(gòu)
  • 定義Vuex認(rèn)證模塊
  • 使用Vuex Store創(chuàng)建Vue 3身份驗(yàn)證組件
  • 使用VeeValidate 4實(shí)現(xiàn)響應(yīng)式表單驗(yàn)證
  • 訪問(wèn)受保護(hù)資源的Vue 3組件
  • 向Vue 3 App添加動(dòng)態(tài)導(dǎo)航欄

出發(fā)!

使用JWT的Vue 3身份驗(yàn)證實(shí)戰(zhàn)

我們將構(gòu)建一個(gè)Vue 3應(yīng)用程序,其中包含:

  • 登錄/注銷(xiāo)、注冊(cè)頁(yè)面。
  • 表單數(shù)據(jù)在發(fā)送到后端之前由前端進(jìn)行驗(yàn)證。
  • 根據(jù)用戶(hù)的角色(管理員、版主、用戶(hù))自動(dòng)更改導(dǎo)航欄項(xiàng)目。

截圖

– 注冊(cè)頁(yè)面:

圖片圖片

– 表單驗(yàn)證如下所示:

圖片圖片

– 登錄頁(yè)面和個(gè)人資料頁(yè)面:

圖片圖片

– 管理員帳戶(hù)的導(dǎo)航欄:

圖片圖片

演示

下面是完整的Vue JWT身份驗(yàn)證App演示(有表單驗(yàn)證、檢查注冊(cè)用戶(hù)名/電子郵件重復(fù)項(xiàng),并使用管理員、版主、用戶(hù)3個(gè)角色測(cè)試授權(quán))。后端REST API使用Spring Boot。

https://www.youtube.com/embed/pPSRVu-Ysjw?rel=0

上面地視頻使用的是Vue 2和VeeValidate 2,邏輯和UI與本教程相同。

用戶(hù)注冊(cè)和用戶(hù)登錄流程

JWT身份驗(yàn)證將調(diào)用2個(gè)接口服務(wù):

  • 用于用戶(hù)注冊(cè)的POST api/auth/signup
  • 用于用戶(hù)登錄的POST api/auth/signin

你可以看看下面的流程,對(duì)Vue客戶(hù)端如何發(fā)出或接收請(qǐng)求和響應(yīng)有一個(gè)大致的了解。

圖片圖片

Vue客戶(hù)端在向受保護(hù)的資源發(fā)送請(qǐng)求之前,必須將JWT添加到HTTP授權(quán)標(biāo)頭中。

Vue App組件圖

現(xiàn)在請(qǐng)看下圖:

圖片圖片

我們知道:

– App組件是一個(gè)具有Router的容器。它從Vuex store/auth獲取應(yīng)用狀態(tài)。然后導(dǎo)航欄可以根據(jù)狀態(tài)來(lái)顯示。App組件還會(huì)將狀態(tài)傳遞給子組件。

– Login和Register組件具有用于提交數(shù)據(jù)的表單(支持vee-validate)。我們調(diào)用Vuex store dispatch()函數(shù)來(lái)執(zhí)行登錄/注冊(cè)操作。

– Vuex操作調(diào)用auth.service方法,auth.service方法將使用axios發(fā)出HTTP請(qǐng)求。這些方法還可以存儲(chǔ)或從瀏覽器本地存儲(chǔ)中獲取JWT。

– home組件對(duì)所有訪客都是公開(kāi)的。

– Profile組件從父組件獲取user數(shù)據(jù)并顯示用戶(hù)信息。

– BoardUser、BoardModerator、BoardAdmin組件將由Vuex狀態(tài)user.roles顯示。這些組件使用user.service獲取來(lái)自API的受保護(hù)的資源。

– user.service使用auth-header()輔助函數(shù)將JWT添加到HTTP授權(quán)標(biāo)頭。auth-header()從本地存儲(chǔ)返回一個(gè)對(duì)象,這個(gè)對(duì)象包含當(dāng)前登錄用戶(hù)的JWT。

技術(shù)

我們將用到以下模塊:

  • vue 3
  • vue-router 4
  • Vuex 4
  • axios:0.21.1
  • VEE-validate 4
  • bootstrap 4
  • vue-fontawesome 3

項(xiàng)目結(jié)構(gòu)

Vue 3身份驗(yàn)證和授權(quán)項(xiàng)目的文件夾和文件結(jié)構(gòu)如下:

圖片圖片

設(shè)置Vue 3項(xiàng)目

在Project文件夾中打開(kāi)cmd,運(yùn)行命令:

vue create vue-3-authentication-jwt

你會(huì)看到一些選項(xiàng),選擇Default ([Vue 3] babel, eslint)。

項(xiàng)目準(zhǔn)備就緒后,運(yùn)行以下命令安裝必要的模塊:

npm install vue-router@4
npm install vuex@4
npm install vee-validate@4 yup
npm install axios
npm install bootstrap@4 jquery popper.js
npm install @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/vue-fontawesome@prerelease

安裝完成后,可以檢查package.json文件中的依賴(lài)項(xiàng)。

"dependencies": {
  "@fortawesome/fontawesome-svg-core": "^1.2.35",
  "@fortawesome/free-solid-svg-icons": "^5.15.3",
  "@fortawesome/vue-fontawesome": "^3.0.0-3",
  "axios": "^0.21.1",
  "bootstrap": "^4.6.0",
  "core-js": "^3.6.5",
  "jquery": "^3.6.0",
  "popper.js": "^1.16.1",
  "vee-validate": "^4.3.5",
  "vue": "^3.0.0",
  "vue-router": "^4.0.6",
  "vuex": "^4.0.0",
  "yup": "^0.32.9"
},

在src文件夾中使用以下代碼創(chuàng)建plugins/font-awesome.js文件:

import { library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import {
  faHome,
  faUser,
  faUserPlus,
  faSignInAlt,
  faSignOutAlt,
} from "@fortawesome/free-solid-svg-icons";
 
library.add(faHome, faUser, faUserPlus, faSignInAlt, faSignOutAlt);
 
export { FontAwesomeIcon };

打開(kāi)src/main.js,如下修改里面的代碼:

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import { FontAwesomeIcon } from './plugins/font-awesome'
 
createApp(App)
  .use(router)
  .use(store)
  .component("font-awesome-icon", FontAwesomeIcon)
  .mount("#app");

可以看到我們導(dǎo)入并應(yīng)用了:

– Vuex的store(稍后在src/store實(shí)現(xiàn))

– Vue Router的router(稍后在src/router.js實(shí)現(xiàn))

– CSS的bootstrap

– 用于圖標(biāo)的vue-fontawesome(稍后在nav中使用)

創(chuàng)建服務(wù)

在src/services文件夾中創(chuàng)建兩個(gè)服務(wù):

圖片圖片

身份驗(yàn)證服務(wù)

該服務(wù)在axios的幫助下為HTTP請(qǐng)求和響應(yīng)提供了三種重要方法:

  • login(): POST {username, password}并將JWT保存到Local Storage
  • logout():刪除來(lái)自Local Storage中的JWT
  • register():POST { username, email, password}
import axios from 'axios';
 
const API_URL = 'http://localhost:8080/api/auth/';
 
class AuthService {
  login(user) {
    return axios
      .post(API_URL + 'signin', {
        username: user.username,
        password: user.password
      })
      .then(response => {
        if (response.data.accessToken) {
          localStorage.setItem('user', JSON.stringify(response.data));
        }
 
        return response.data;
      });
  }
 
  logout() {
    localStorage.removeItem('user');
  }
 
  register(user) {
    return axios.post(API_URL + 'signup', {
      username: user.username,
      email: user.email,
      password: user.password
    });
  }
}
 
export default new AuthService();

數(shù)據(jù)服務(wù)

還有從服務(wù)器檢索數(shù)據(jù)的方法。如果要訪問(wèn)受保護(hù)的資源,那么HTTP請(qǐng)求需要Authorization標(biāo)頭。

在auth-header.js中創(chuàng)建輔助函數(shù)authHeader():

export default function authHeader() {
  let user = JSON.parse(localStorage.getItem('user'));
 
  if (user && user.accessToken) {
    return { Authorization: 'Bearer ' + user.accessToken };
  } else {
    return {};
  }
}

它檢查user項(xiàng)的Local Storage。

如果存在使用accessToken(JWT)登錄的user,則返回HTTP Authorization標(biāo)頭。否則返回空對(duì)象。

注意:對(duì)于Node.js Express后端,請(qǐng)使用x-access-token標(biāo)頭,如下所示:

export default function authHeader() {
  let user = JSON.parse(localStorage.getItem('user'));
 
  if (user && user.accessToken) {
    // for Node.js Express back-end
    return { 'x-access-token': user.accessToken };
  } else {
    return {};
  }
}

接著在user.service.js中定義用于訪問(wèn)數(shù)據(jù)的服務(wù):

import axios from 'axios';
import authHeader from './auth-header';
 
const API_URL = 'http://localhost:8080/api/test/';
 
class UserService {
  getPublicContent() {
    return axios.get(API_URL + 'all');
  }
 
  getUserBoard() {
    return axios.get(API_URL + 'user', { headers: authHeader() });
  }
 
  getModeratorBoard() {
    return axios.get(API_URL + 'mod', { headers: authHeader() });
  }
 
  getAdminBoard() {
    return axios.get(API_URL + 'admin', { headers: authHeader() });
  }
}
 
export default new UserService();

可以看到,在請(qǐng)求授權(quán)的資源時(shí),我們?cè)赼uthHeader()函數(shù)的幫助下添加了HTTP標(biāo)頭。

定義Vuex認(rèn)證模塊

我們將用于身份驗(yàn)證的Vuex模塊放在src/store文件夾。

圖片圖片

現(xiàn)在打開(kāi)index.js文件,將auth.module導(dǎo)入到主Vuex Store。

import { createStore } from "vuex";
import { auth } from "./auth.module";
 
const store = createStore({
  modules: {
    auth,
  },
});
 
export default store;

然后開(kāi)始定義Vuex身份驗(yàn)證模塊,其中包含:

  • state: { status, user }
  • actions: { login, logout, register }
  • mutations: { loginSuccess, loginFailure, logout, registerSuccess, registerFailure }

我們使用上面定義的AuthService來(lái)發(fā)出身份驗(yàn)證請(qǐng)求。

auth.module.js

import AuthService from '../services/auth.service';
 
const user = JSON.parse(localStorage.getItem('user'));
const initialState = user
  ? { status: { loggedIn: true }, user }
  : { status: { loggedIn: false }, user: null };
 
export const auth = {
  namespaced: true,
  state: initialState,
  actions: {
    login({ commit }, user) {
      return AuthService.login(user).then(
        user => {
          commit('loginSuccess', user);
          return Promise.resolve(user);
        },
        error => {
          commit('loginFailure');
          return Promise.reject(error);
        }
      );
    },
    logout({ commit }) {
      AuthService.logout();
      commit('logout');
    },
    register({ commit }, user) {
      return AuthService.register(user).then(
        response => {
          commit('registerSuccess');
          return Promise.resolve(response.data);
        },
        error => {
          commit('registerFailure');
          return Promise.reject(error);
        }
      );
    }
  },
  mutations: {
    loginSuccess(state, user) {
      state.status.loggedIn = true;
      state.user = user;
    },
    loginFailure(state) {
      state.status.loggedIn = false;
      state.user = null;
    },
    logout(state) {
      state.status.loggedIn = false;
      state.user = null;
    },
    registerSuccess(state) {
      state.status.loggedIn = false;
    },
    registerFailure(state) {
      state.status.loggedIn = false;
    }
  }
};

創(chuàng)建Vue 3認(rèn)證組件

繼續(xù)身份驗(yàn)證組件,這些組件應(yīng)該與Vuex Store一起使用,而不是直接使用axios或AuthService:

– 使用this.$store.state.auth獲取status

– 通過(guò)調(diào)度操作this.$store.dispatch()發(fā)出請(qǐng)求

圖片圖片

Vue 3登錄頁(yè)面

在src/components文件夾使用以下代碼創(chuàng)建Login.vue文件:

<template>
  <div class="col-md-12">
    <div class="card card-container">
      <img
        id="profile-img"
        src="http://ssl.gstatic.com/accounts/ui/avatar_2x.png"
        class="profile-img-card"
      />
      <Form @submit="handleLogin" :validation-schema="schema">
        <div class="form-group">
          <label for="username">Username</label>
          <Field name="username" type="text" class="form-control" />
          <ErrorMessage name="username" class="error-feedback" />
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <Field name="password" type="password" class="form-control" />
          <ErrorMessage name="password" class="error-feedback" />
        </div>
 
        <div class="form-group">
          <button class="btn btn-primary btn-block" :disabled="loading">
            <span
              v-show="loading"
              class="spinner-border spinner-border-sm"
            ></span>
            <span>Login</span>
          </button>
        </div>
 
        <div class="form-group">
          <div v-if="message" class="alert alert-danger" role="alert">
            {{ message }}
          </div>
        </div>
      </Form>
    </div>
  </div>
</template>
 
<script>
import { Form, Field, ErrorMessage } from "vee-validate";
import * as yup from "yup";
 
export default {
  name: "Login",
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    const schema = yup.object().shape({
      username: yup.string().required("Username is required!"),
      password: yup.string().required("Password is required!"),
    });
 
    return {
      loading: false,
      message: "",
      schema,
    };
  },
  computed: {
    loggedIn() {
      return this.$store.state.auth.status.loggedIn;
    },
  },
  created() {
    if (this.loggedIn) {
      this.$router.push("/profile");
    }
  },
  methods: {
    handleLogin(user) {
      this.loading = true;
 
      this.$store.dispatch("auth/login", user).then(
        () => {
          this.$router.push("/profile");
        },
        (error) => {
          this.loading = false;
          this.message =
            (error.response &&
              error.response.data &&
              error.response.data.message) ||
            error.message ||
            error.toString();
        }
      );
    },
  },
};
</script>
 
<style scoped>
...
</style>

此頁(yè)面有一個(gè)包含2個(gè)Field,即username和password的Form。使用VeeValidate 4.x來(lái)驗(yàn)證輸入,如果存在無(wú)效字段,則顯示錯(cuò)誤消息。

我們使用Vuex Store—this.$store.state.auth.status.loggedIn檢查用戶(hù)登錄狀態(tài)。如果狀態(tài)為true,則使用Vue Router將用戶(hù)定向到Profile頁(yè)面:

created() {
  if (this.loggedIn) {
    this.$router.push('/profile');
  }
},

在handleLogin()函數(shù)中,我們將'auth/login' Action調(diào)度到Vuex Store。如果登錄成功,則轉(zhuǎn)到Profile頁(yè)面,否則顯示錯(cuò)誤消息。

Vue 3注冊(cè)頁(yè)面

注冊(cè)頁(yè)面類(lèi)似于登錄頁(yè)面。

不一樣的是,表單驗(yàn)證需要提供更多詳細(xì)信息:

  • username:必填,最小長(zhǎng)度:3,最大長(zhǎng)度:20
  • email: 必填, email, 最大長(zhǎng)度:50
  • password:必填,最小長(zhǎng)度:6,最大長(zhǎng)度:40

而表單提交,則調(diào)度'auth/register' Vuex Action。

components/Register.vue

<template>
  <div class="col-md-12">
    <div class="card card-container">
      <img
        id="profile-img"
        src="http://ssl.gstatic.com/accounts/ui/avatar_2x.png"
        class="profile-img-card"
      />
      <Form @submit="handleRegister" :validation-schema="schema">
        <div v-if="!successful">
          <div class="form-group">
            <label for="username">Username</label>
            <Field name="username" type="text" class="form-control" />
            <ErrorMessage name="username" class="error-feedback" />
          </div>
          <div class="form-group">
            <label for="email">Email</label>
            <Field name="email" type="email" class="form-control" />
            <ErrorMessage name="email" class="error-feedback" />
          </div>
          <div class="form-group">
            <label for="password">Password</label>
            <Field name="password" type="password" class="form-control" />
            <ErrorMessage name="password" class="error-feedback" />
          </div>
 
          <div class="form-group">
            <button class="btn btn-primary btn-block" :disabled="loading">
              <span
                v-show="loading"
                class="spinner-border spinner-border-sm"
              ></span>
              Sign Up
            </button>
          </div>
        </div>
      </Form>
 
      <div
        v-if="message"
        class="alert"
        :class="successful ? 'alert-success' : 'alert-danger'"
      >
        {{ message }}
      </div>
    </div>
  </div>
</template>
 
<script>
import { Form, Field, ErrorMessage } from "vee-validate";
import * as yup from "yup";
 
export default {
  name: "Register",
  components: {
    Form,
    Field,
    ErrorMessage,
  },
  data() {
    const schema = yup.object().shape({
      username: yup
        .string()
        .required("Username is required!")
        .min(3, "Must be at least 3 characters!")
        .max(20, "Must be maximum 20 characters!"),
      email: yup
        .string()
        .required("Email is required!")
        .email("Email is invalid!")
        .max(50, "Must be maximum 50 characters!"),
      password: yup
        .string()
        .required("Password is required!")
        .min(6, "Must be at least 6 characters!")
        .max(40, "Must be maximum 40 characters!"),
    });
 
    return {
      successful: false,
      loading: false,
      message: "",
      schema,
    };
  },
  computed: {
    loggedIn() {
      return this.$store.state.auth.status.loggedIn;
    },
  },
  mounted() {
    if (this.loggedIn) {
      this.$router.push("/profile");
    }
  },
  methods: {
    handleRegister(user) {
      this.message = "";
      this.successful = false;
      this.loading = true;
 
      this.$store.dispatch("auth/register", user).then(
        (data) => {
          this.message = data.message;
          this.successful = true;
          this.loading = false;
        },
        (error) => {
          this.message =
            (error.response &&
              error.response.data &&
              error.response.data.message) ||
            error.message ||
            error.toString();
          this.successful = false;
          this.loading = false;
        }
      );
    },
  },
};
</script>
 
<style scoped>
...
</style>

Profile頁(yè)面

此頁(yè)面從Vuex Store獲取當(dāng)前用戶(hù)并顯示信息。如果用戶(hù)未登錄,則定向到登錄頁(yè)面。

components/Profile.vue

<template>
  <div class="container">
    <header class="jumbotron">
      <h3>
        <strong>{{currentUser.username}}</strong> Profile
      </h3>
    </header>
    <p>
      <strong>Token:</strong>
      {{currentUser.accessToken.substring(0, 20)}} ... {{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}}
    </p>
    <p>
      <strong>Id:</strong>
      {{currentUser.id}}
    </p>
    <p>
      <strong>Email:</strong>
      {{currentUser.email}}
    </p>
    <strong>Authorities:</strong>
    <ul>
      <li v-for="role in currentUser.roles" :key="role">{{role}}</li>
    </ul>
  </div>
</template>
 
<script>
export default {
  name: 'Profile',
  computed: {
    currentUser() {
      return this.$store.state.auth.user;
    }
  },
  mounted() {
    if (!this.currentUser) {
      this.$router.push('/login');
    }
  }
};
</script>

創(chuàng)建用于訪問(wèn)資源的Vue組件

這些組件將使用UserService來(lái)請(qǐng)求數(shù)據(jù)。

圖片圖片

主頁(yè)

這是一個(gè)公共頁(yè)面。

components/Home.vue

<template>
  <div class="container">
    <header class="jumbotron">
      <h3>{{ content }}</h3>
    </header>
  </div>
</template>
 
<script>
import UserService from "../services/user.service";
 
export default {
  name: "Home",
  data() {
    return {
      content: "",
    };
  },
  mounted() {
    UserService.getPublicContent().then(
      (response) => {
        this.content = response.data;
      },
      (error) => {
        this.content =
          (error.response &&
            error.response.data &&
            error.response.data.message) ||
          error.message ||
          error.toString();
      }
    );
  },
};
</script>

基于角色的頁(yè)面

我們有3個(gè)頁(yè)面用于訪問(wèn)受保護(hù)的數(shù)據(jù):

  • BoardUser頁(yè)面調(diào)用UserService.getUserBoard()
  • BoardModerator頁(yè)面調(diào)用UserService.getModeratorBoard()
  • BoardAdmin頁(yè)面調(diào)用UserService.getAdminBoard()

請(qǐng)看下面的示例。

components/BoardUser.vue

<template>
  <div class="container">
    <header class="jumbotron">
      <h3>{{ content }}</h3>
    </header>
  </div>
</template>
 
<script>
import UserService from "../services/user.service";
 
export default {
  name: "User",
  data() {
    return {
      content: "",
    };
  },
  mounted() {
    UserService.getUserBoard().then(
      (response) => {
        this.content = response.data;
      },
      (error) => {
        this.content =
          (error.response &&
            error.response.data &&
            error.response.data.message) ||
          error.message ||
          error.toString();
      }
    );
  },
};
</script>

定義Vue Router的路由

現(xiàn)在我們?yōu)閂ue 3應(yīng)用程序定義所有路由。

src/router.js

import { createWebHistory, createRouter } from "vue-router";
import Home from "./components/Home.vue";
import Login from "./components/Login.vue";
import Register from "./components/Register.vue";
// lazy-loaded
const Profile = () => import("./components/Profile.vue")
const BoardAdmin = () => import("./components/BoardAdmin.vue")
const BoardModerator = () => import("./components/BoardModerator.vue")
const BoardUser = () => import("./components/BoardUser.vue")
 
const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
  },
  {
    path: "/home",
    component: Home,
  },
  {
    path: "/login",
    component: Login,
  },
  {
    path: "/register",
    component: Register,
  },
  {
    path: "/profile",
    name: "profile",
    // lazy-loaded
    component: Profile,
  },
  {
    path: "/admin",
    name: "admin",
    // lazy-loaded
    component: BoardAdmin,
  },
  {
    path: "/mod",
    name: "moderator",
    // lazy-loaded
    component: BoardModerator,
  },
  {
    path: "/user",
    name: "user",
    // lazy-loaded
    component: BoardUser,
  },
];
 
const router = createRouter({
  history: createWebHistory(),
  routes,
});
 
export default router;

向Vue app添加導(dǎo)航欄

這是應(yīng)用程序中包含導(dǎo)航欄的根容器。我們要添加router-view。

src/App.vue

<template>
  <div id="app">
    <nav class="navbar navbar-expand navbar-dark bg-dark">
      <a href="/" class="navbar-brand">bezKoder</a>
      <div class="navbar-nav mr-auto">
        <li class="nav-item">
          <router-link to="/home" class="nav-link">
            <font-awesome-icon icon="home" /> Home
          </router-link>
        </li>
        <li v-if="showAdminBoard" class="nav-item">
          <router-link to="/admin" class="nav-link">Admin Board</router-link>
        </li>
        <li v-if="showModeratorBoard" class="nav-item">
          <router-link to="/mod" class="nav-link">Moderator Board</router-link>
        </li>
        <li class="nav-item">
          <router-link v-if="currentUser" to="/user" class="nav-link">User</router-link>
        </li>
      </div>
 
      <div v-if="!currentUser" class="navbar-nav ml-auto">
        <li class="nav-item">
          <router-link to="/register" class="nav-link">
            <font-awesome-icon icon="user-plus" /> Sign Up
          </router-link>
        </li>
        <li class="nav-item">
          <router-link to="/login" class="nav-link">
            <font-awesome-icon icon="sign-in-alt" /> Login
          </router-link>
        </li>
      </div>
 
      <div v-if="currentUser" class="navbar-nav ml-auto">
        <li class="nav-item">
          <router-link to="/profile" class="nav-link">
            <font-awesome-icon icon="user" />
            {{ currentUser.username }}
          </router-link>
        </li>
        <li class="nav-item">
          <a class="nav-link" @click.prevent="logOut">
            <font-awesome-icon icon="sign-out-alt" /> LogOut
          </a>
        </li>
      </div>
    </nav>
 
    <div class="container">
      <router-view />
    </div>
  </div>
</template>
 
<script>
export default {
  computed: {
    currentUser() {
      return this.$store.state.auth.user;
    },
    showAdminBoard() {
      if (this.currentUser && this.currentUser['roles']) {
        return this.currentUser['roles'].includes('ROLE_ADMIN');
      }
 
      return false;
    },
    showModeratorBoard() {
      if (this.currentUser && this.currentUser['roles']) {
        return this.currentUser['roles'].includes('ROLE_MODERATOR');
      }
 
      return false;
    }
  },
  methods: {
    logOut() {
      this.$store.dispatch('auth/logout');
      this.$router.push('/login');
    }
  }
};
</script>

使用font-awesome-icon可以使得導(dǎo)航欄看起來(lái)更專(zhuān)業(yè)。

而且導(dǎo)航欄還可以根據(jù)從Vuex Store state檢索到的當(dāng)前用戶(hù)的roles而動(dòng)態(tài)變化。

處理未經(jīng)授權(quán)的訪問(wèn)

如果你想在每次觸發(fā)導(dǎo)航操作時(shí)檢查授權(quán)狀態(tài),只需在src/router.js中添加router.beforeEach(),如下所示:

router.beforeEach((to, from, next) => {
  const publicPages = ['/login', '/register', '/home'];
  const authRequired = !publicPages.includes(to.path);
  const loggedIn = localStorage.getItem('user');
 
  // trying to access a restricted page + not logged in
  // redirect to login page
  if (authRequired && !loggedIn) {
    next('/login');
  } else {
    next();
  }
});

為Vue App配置端口

由于大多數(shù)HTTP Server使用CORS配置,接受僅限于某些站點(diǎn)或端口的資源共享,因此我們還需要為App配置端口。

在項(xiàng)目根文件夾中,創(chuàng)建包含以下內(nèi)容的vue.config.js文件:

module.exports = {
  devServer: {
    port: 8081
  }
}

我們將app設(shè)置為運(yùn)行在端口8081上。

結(jié)論

今天,我們學(xué)習(xí)了很多有趣的內(nèi)容,學(xué)習(xí)了如何使用Axios、Vuex和Vue Router構(gòu)建支持JWT身份驗(yàn)證和授權(quán)的Vue應(yīng)用程序。

責(zé)任編輯:武曉燕 來(lái)源: 前端新世界
相關(guān)推薦

2024-05-17 09:51:11

2020-08-04 08:04:46

VueAPI驗(yàn)證

2024-02-02 08:56:54

2024-02-23 07:18:40

JWTWeb應(yīng)用程序

2010-11-30 15:31:38

SharePoint Kerberos

2024-08-07 12:14:39

2024-04-01 00:00:00

信息JWT密碼

2020-12-17 08:10:19

身份驗(yàn)證授權(quán)微服務(wù)

2024-11-06 10:16:22

2010-09-06 11:24:47

CHAP驗(yàn)證PPP身份驗(yàn)證

2015-01-21 09:15:44

2020-08-23 09:04:04

SSH身份驗(yàn)證FIDO2 USB

2022-10-31 10:00:00

2012-04-10 09:36:58

2012-02-20 09:55:41

ibmdw

2024-07-30 12:00:06

2025-04-25 07:00:00

身份驗(yàn)證CISO無(wú)密碼

2011-02-21 10:54:45

2013-07-21 18:32:13

iOS開(kāi)發(fā)ASIHTTPRequ

2021-05-11 19:58:01

身份驗(yàn)證Code
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)