jwt+vuex+localStorage+beforeEach實現登入驗證

jwt+vuex+localStorage+beforeEach實現登入驗證

  1. 涉及到的知識點
    ① 後台介面的建置,以及解決跨域問題
    ② vuex的安裝以及是使用
    ③ 路由以及路由守衛的應用
    ④ axios網路請求
    ⑤ jsonwebtoken登入許可權驗證
    ⑥ localStorage 本地儲存
  • 一 1. 構建專案vuex-anli,npm 新增login登入路由
    router檔案
1
2
3
4
  {<!-- -->
    path:'/login',
    component:()=>import('../views/login')
  }

  • 2 .構建login頁面結構,安裝引入 axios
    login檔案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
  <div>
     使用者名稱:<input type="text" v-model="username"><br>
      密碼:<input type="text" v-model="pwd">
      <button @click="login">登入</button>
  </div>
</template>
<script>
import axios from 'axios'
export default {<!-- -->
  data(){<!-- -->
      return{<!-- -->
          username:'',
          pwd:''
      }
  },
  methods:{<!-- -->
      login(){<!-- -->
          axios.post('http://localhost:5000/login','username='+this.usernamr
          +'&pwd='+this.pwd)
          .then(data=>{<!-- -->
              console.log(data);
          })
      }
  }
}
</script>
1
npm i -S axios
  • 二 1. 建立server資料夾 構建後台
  • post請求 引入bodyParser,引入cors後台解決跨域,在後台給定一個user物件,用來驗證登入是否正確,成功後回傳一個token值
    token:jwt.sign({ })這裡面寫荷載資料,回傳一個隨機生成的id和username,在定義一個加密的字串 let jiami

server檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let express = require('express');
let bodyParser = require('body-parser')
let cors = require('cors');
let jwt = require('jsonwebtoken')

let user = {<!-- -->
    username:'kevin',
    pwd:123
}
let jiami ='idhsidhcsdjmsldmvpsdmdss'

let app =express();
app.use(cors())
app.use(bodyParser.urlencoded({<!-- -->extended:false}))

app.post('/login',(req,res)=>{<!-- -->
   if(req.body.username == user.username && req.body.pwd == user.pwd){<!-- -->
       res.send({<!-- -->
           msg:'success',
           token:jwt.sign({<!-- -->
               id:Math.floor(Math.random()*100000),
               username:user.username
           },jiami)

       })
   }else{<!-- -->
       res.send({<!-- -->
           msg:'登入失敗'
       })
   }
})

app.listen(5000,function(){<!-- -->
    console.log(5000);
})
1
2
安裝 express cors jsonwebtoken
npm i -S express cors jsonwebtoken
  • 三 1. 進行登入許可權驗證,在router頁面,新增路由源訊息(登入狀態),使用路由前置守衛beforeEach,先看要跳轉的路徑,要不要有登入狀態,if(to.meta.islogin),看登入狀態islogin,如果有還為ture,那麼就從vuex中取得資料去,否則跳轉到登入頁面
    router部分程式碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//路由前置守衛
router.beforeEach((to,from,next)=>{<!-- -->
  if(to.meta.islogin){<!-- -->
    //從vuex中取得登入狀態
     let vuex_islogin=store.state.islogin
     if(vuex_islogin){<!-- -->
       next()
     }else{<!-- -->
       next('/login')
     }
  }else{<!-- -->
     next()
  }
})

  • 2.在store中,定義登入狀態islogin:false,以及改變登入狀態的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({<!-- -->
  state: {<!-- -->
    islogin:false
  },
  mutations: {<!-- -->
    changeIsLogin(state){<!-- -->
      state.islogin=true
    }
  },
})
  • 注意:路由還沒跳轉哪,在beforeEach中訪問不到this,這時需要把store用impo引進來
  • 3 點選登入後判斷回傳的msg狀態,成功的話,呼叫store中的方法,改變state中定義的狀態值
  • 4.把token儲存到本地,並回傳上一級
    login部分程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  methods:{<!-- -->
      login(){<!-- -->
          axios.post('http://localhost:5000/login','username='+this.username
          +'&pwd='+this.pwd)
          .then(data=>{<!-- -->
              console.log(data);
              if(data.data.msg == 'success'){<!-- -->
                  alert(data.data.msg)
                  this.$store.commit('changeIsLogin')
                  //把token儲存到本地
                  localStorage.setItem('vuex_login_token',data.data.token)
                  //回傳上一層面
                  this.$router.back();
              }else{<!-- -->
                  alert(data.data.msg)
              }
          })
      }
  }

問題:清除快取後就又回到login頁面,從本地取得token,如果有說明登入過,然後把vuex的state更改為ture
main.js 部分程式碼

1
2
3
4
5
//從本地取得token,如果有說明登入過,然後把vuex的state更改為ture
let islogin = localStorage.getItem("vuex_login_token");
if(islogin){<!-- -->
  store.commit('changeIsLogin')
}

目錄結構:


完整程式碼如下
server.js程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let express = require('express');
let bodyParser = require('body-parser')
let cors = require('cors');
let jwt = require('jsonwebtoken')

let user = {<!-- -->
    username:'kevin',
    pwd:123
}
let jiami ='idhsidhcsdjmsldmvpsdmdss'

let app =express();
app.use(cors())
app.use(bodyParser.urlencoded({<!-- -->extended:false}))

app.post('/login',(req,res)=>{<!-- -->
   if(req.body.username == user.username && req.body.pwd == user.pwd){<!-- -->
       res.send({<!-- -->
           msg:'success',
           token:jwt.sign({<!-- -->
               id:Math.floor(Math.random()*100000),
               username:user.username
           },jiami)

       })
   }else{<!-- -->
       res.send({<!-- -->
           msg:'登入失敗'
       })
   }
})

app.listen(5000,function(){<!-- -->
    console.log(5000);
})

router index.js程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

import store from '../store/index'

Vue.use(VueRouter)

const routes = [
  {<!-- -->
    path: '/',
    name: 'Home',
    component: Home,
    meta:{<!-- -->
      islogin:true
    }
  },
  {<!-- -->
    path: '/about',
    name: 'About',
    meta:{<!-- -->
      islogin:true
    },
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {<!-- -->
    path:'/login',
    component:()=>import('../views/login')
  }
]

const router = new VueRouter({<!-- -->
  routes
})

//路由前置守衛
router.beforeEach((to,from,next)=>{<!-- -->
  if(to.meta.islogin){<!-- -->
    //從vuex中取得登入狀態
     let vuex_islogin=store.state.islogin
     if(vuex_islogin){<!-- -->
       next()
     }else{<!-- -->
       next('/login')
     }
  }else{<!-- -->
     next()
  }
})

export default router

store index.j』s程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({<!-- -->
  state: {<!-- -->
    islogin:false
  },
  mutations: {<!-- -->
    changeIsLogin(state){<!-- -->
      state.islogin=true
    }
  },
})

login程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
  <div>
     使用者名稱:<input type="text" v-model="username"><br>
      密碼:<input type="text" v-model="pwd">
      <button @click="login">登入</button>
  </div>
</template>
<script>
import axios from 'axios'
export default {<!-- -->
  data(){<!-- -->
      return{<!-- -->
          username:'',
          pwd:''
      }
  },
  methods:{<!-- -->
      login(){<!-- -->
          axios.post('http://localhost:5000/login','username='+this.username
          +'&pwd='+this.pwd)
          .then(data=>{<!-- -->
              console.log(data);
              if(data.data.msg == 'success'){<!-- -->
                  alert(data.data.msg)
                  this.$store.commit('changeIsLogin')
                  //把token儲存到本地
                  localStorage.setItem('vuex_login_token',data.data.token)
                  //回傳上一層面
                  this.$router.back();
              }else{<!-- -->
                  alert(data.data.msg)
              }
          })
      }
  }
}
</script>

main.js程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false
//從本地取得token,如果有說明登入過,然後把vuex的state更改為ture
let islogin = localStorage.getItem("vuex_login_token");
if(islogin){<!-- -->
  store.commit('changeIsLogin')
}

new Vue({<!-- -->
  router,
  store,
  render: h => h(App)
}).$mount('#app')