router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from './store/store'
import * as types from './store/types'
import Index from './index.vue'
import Repository from './repository.vue'
import Login from './login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: '/',
component: Index
},
{
path: '/repository',
name: 'repository',
meta: {
requireAuth: true,
},
component: Repository
},
{
path: '/login',
name: 'login',
component: Login
}
];
// 页面刷新时,重新赋值token
if (window.localStorage.getItem('token')) {
store.commit(types.LOGIN, window.localStorage.getItem('token'))
}
const router = new VueRouter({
routes
});
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.requireAuth)) {
if (store.state.token) {
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath}
})
}
}
else {
next();
}
})
export default router;
多个请求 401 的情况,重定向到登录页面 query 参数错误
http.js
import axios from 'axios'
import store from './store/store'
import * as types from './store/types'
import router from './router'
// axios 配置
axios.defaults.timeout = 5000
axios.defaults.baseURL = 'https://api.github.com'
// http request 拦截器
axios.interceptors.request.use(
config => {
if (store.state.token) {
config.headers.Authorization = `token ${store.state.token}`
}
return config
},
err => {
return Promise.reject(err)
},
)
// http response 拦截器
axios.interceptors.response.use(
response => {
return response
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 401 清除token信息并跳转到登录页面
store.commit(types.LOGOUT)
// 只有在当前路由不是登录页面才跳转
router.currentRoute.path !== 'login' &&
router.replace({
path: 'login',
query: { redirect: router.currentRoute.path },
})
}
}
// console.log(JSON.stringify(error));//console : Error: Request failed with status code 402
return Promise.reject(error.response.data)
},
)
export default axios
login.vue
<template>
<div class="container">
<form class="login-form" novalidate @submit.stop.prevent="login">
<md-input-container md-has-password>
<label>Github Personal Token(Press Enter)</label>
<md-input type="password" v-model="token"></md-input>
</md-input-container>
</form>
<md-button href="https://github.com/settings/tokens/new" target="_blank" class="md-raised md-primary">generate your token</md-button>
</div>
</template>
<script type="application/ecmascript">
import * as types from './store/types'
export default {
name: '',
data () {
return {
msg: '',
token: ''
}
},
mounted(){
this.$store.commit(types.TITLE, 'Login');
},
methods: {
login(){
if (this.token) {
this.$store.commit(types.LOGIN, this.token)
let redirect = decodeURIComponent(this.$route.query.redirect || '/');
this.$router.push({
path: redirect
})
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang='scss' rel="stylesheet/scss" type="text/css">
.login-form{
width: 400px;
margin: 50px auto;
}
</style>
app.vue
<template>
<div id="app">
<md-toolbar class="black">
<div class="container">
<div class="left">
<a href="https://github.com" target="_blank">
<svg aria-hidden="true" class="octicon octicon-mark-github" height="32" version="1.1"
viewBox="0 0 16 16" width="32">
<path fill-rule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
</svg>
</a>
<span class="md-title title">{{title}}</span>
</div>
<div class="right">
<a v-on:click="logout" class=" md-default" v-show="token">logout</a>
</div>
</div>
</md-toolbar>
<router-view>
</router-view>
</div>
</template>
<script>
import * as types from './store/types'
import {mapState} from 'vuex'
export default {
name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App',
}
},
computed: mapState({
title: state => state.title,
token: state => state.token
}),
methods: {
logout(){
this.$store.commit(types.LOGOUT)
this.$router.push({
path: '/'
})
}
}
}
</script>
<style lang="scss">
@import "../node_modules/vue-material/dist/vue-material.css";
@import "assets/css.css";
@import "assets/icon.css";
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
.container {
width: 1000px;
margin: 0 auto;
}
.left {
float: left;
text-align: left;
}
.right{
float: right;
}
.md-toolbar .title {
vertical-align: text-top;
margin-left: 5px;
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
store.js
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './types'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {},
token: null,
title: ''
},
mutations: {
[types.LOGIN]: (state, data) => {
localStorage.token = data;
state.token = data;
},
[types.LOGOUT]: (state) => {
localStorage.removeItem('token');
state.token = null
},
[types.TITLE]: (state, data) => {
state.title = data;
}
}
})
type.js
export const LOGIN = 'login';
export const LOGOUT = 'logout';
export const TITLE = 'title'