一. 安裝vuex及永久化vuex)
npm i vuex
npm install --save vuex-persistedstate
在 src 目錄下 創(chuàng)建 "store" 文件夾, 并在文件夾下創(chuàng)建index.js文件, 代碼如下:
import Vue from 'vue'; import Vuex from 'vuex'; import createPersistedState from 'vuex-persistedstate'; Vue.use(Vuex); const state = { 'token' : '' }; const mutations = { changeToken(state,params){ state.token = params; } }; const store = new Vuex.Store({ plugins:[createPersistedState()], state : state, //狀態(tài)管理 mutations:mutations, //修改state modules:{} //模塊 }); export default store; //main.js文件引入 import store from './store';
二. 安裝Element組件 及axios組件
npm i element-ui -S
npm install axios --save
main.js文件引入
import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import axios from 'axios'; axios.defaults.baseURL=' new Vue({ router, store, //注入,組件中可以使用 this.$store 獲取 render: h => h(App) }).$mount('#app');
三. views下創(chuàng)建login.vue組件, 代碼如下:
<template> <div> <Header></Header> <el-form ref="form" label-width="80px" class="form"> <el-form-item label="用戶名"> <el-input v-model="form.username" clearable></el-input> </el-form-item> <el-form-item label="密碼" > <el-input v-model="form.userpwd" show-password></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login">登陸</el-button> <el-button>取消</el-button> </el-form-item> </el-form> </div> </template> <script> console.log(localStorage.getItem('preRoute')); import Header from "@/components/Header" export default{ data() { return { form: { username: '', userpwd : '' }, } }, components:{ Header }, methods:{ login(){ if(this.form.username == ''){ this.$message({ message: '用戶名不能為空', type: 'warning',offset:200}); return false; } if(this.form.userpwd == ''){ this.$message({ message: '用戶密碼不能為空', type: 'warning',offset:200}); return false; } // console.log(this.form); // return ; this.axios.post("loginCheck",{params:this.form}).then(res=>{ let _this = this; if(res.data.code == 1){ this.$store.commit("changeToken",res.data.token); this.$message({ message: '登陸成功', type: 'success', offset:200, onClose:()=>{ // Object.keys(_this.data).forEach(key=>{_this.data[key]=''}) } }); const curr = localStorage.getItem('preRoute') if (curr == null) { this.$router.push({ path: "/" }); } else { this.$router.push({ path: curr });//跳轉(zhuǎn)到來源頁面 } } else{ this.$message({ message: '賬號密碼不正確', type: 'warning', offset:200 }); } }) } } } </script> <style> .form{ width:300px; margin: 20px auto; } </style>
四. 創(chuàng)建安全退出文件Exit.vue
<template> <div> <Header></Header> </div> </template> <script> import Header from "@/components/Header" export default{ components:{ Header }, mounted(){ this.$store.commit("changeToken",""); this.$message({ message: '安全退出', type: 'success', offset:200, onClose:()=>{ this.$router.push({ path: "/"}); //此處寫提示關(guān)閉后需要執(zhí)行的函數(shù) } }); } } </script>
五, 配置路由及導(dǎo)航守衛(wèi)
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Store from '../store' import axios from 'axios'; Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home, meta: {login_require:true} }, { path: '/contact', name: 'Cantact', component:()=>import('../views/Contact.vue'), meta: {login_require:true} }, { path:'/message', name:'Message', component:()=>import('../views/Message.vue'), meta: {login_require:true} }, { path:'/about', name:'About', component:()=>import('../views/About.vue'), meta: {login_require:true} }, { path:'/news', name:'News', component:()=>import("../views/News.vue"), meta: {login_require:true} }, { path:'/newsdetail', name:'Newsdetail', component:()=>import("../views/Newsdetail.vue"), meta: {login_require:true} }, { path:'/vuexx', name:'Vuexx', component:() => import('../views/Vuexx.vue'), meta: {login_require:true} }, { path:'/vuea', name:'Vuea', component:() => import('../views/Vuea.vue'), meta: {login_require:true} }, { path:'/eleform', name:'Eleform', component:()=> import('../views/Eleform.vue'), meta: {login_require:true} }, { path:'/mysecret', name:'Mysecret', component:()=> import('../views/mysecret.vue'), meta: {login_require:false} }, { path:'/login', name:"Login", component:()=> import('../views/login.vue'), meta: {login_require:true} }, { path:'/exit', name:"Exit", component:()=> import('../views/Exit.vue'), meta: {login_require:true} } ] const router = new VueRouter({ routes }) router.beforeEach((to,from,next)=>{ let token = Store.state.token; if ((to.matched.some(function (item) { return item.meta.login_require }))) { next(); } else { if(token == ""){ //沒有得到token, 所以去登陸 localStorage.setItem("preRoute", router.currentRoute.fullPath); //保存當(dāng)前路徑 if(to.path == "/login" || to.path == "/exit"){ next(); } else{ next("/login"); } } else{ //有了token值, 然后判斷是否是正確的, 有沒有過期等 axios({ method: 'get', url: "http://ggqvue.cn/vue/jwtTokenVerify", headers: { 'Authorization': token } //params:{token:token} }).then(res => { if(res.data.code == 2){ //驗證有問題 // console.log("驗證不通過"); localStorage.setItem("preRoute", router.currentRoute.fullPath) if(to.path == "/login" || to.path == "/exit"){ next(); } else{ next("/login"); } } else{ // console.log("驗證通過"); next(); } }).catch(e => { }) } } }) export default router
六. 后臺登陸及token驗證
public function loginCheck(){ $post = $this->request->post("params"); // halt($post); //模擬比較, 實際應(yīng)用中通常 從數(shù)據(jù)庫中查詢比對 if($post["username"] == "admin" && $post["userpwd"] == "123456"){ $payload=[ 'iss'=>'莊子', 'iat'=>time(), 'exp'=>time()+7200, 'nbf'=>time(), 'sub'=>'用戶登陸操作', 'jti'=>md5(uniqid('JWT').time()), "username"=>$post["username"] ]; $token=\Jwt::getToken($payload); return json(["code"=>1, "mes"=>"登陸成功","token"=>$token]); } else{ return json(["code"=>2, "mes"=>"登陸失敗"]); } } public function jwtTokenVerify(){ $token = $this->request->header("Authorization"); //對token進(jìn)行驗證簽名 $result = \Jwt::verifyToken($token); if($result["code"] == 1){ return json(["code"=>1,"mes"=>"success", "username"=>$result["payload"]["username"]]); } else{ return json(["code"=>2,"mes"=>"fail"]); } }
注意: php端解決跨域操作的方法,在入口文件中加入以下內(nèi)容:
header("Access-Control-Allow-Origin:*"); header("Access-Control-Allow-Methods:GET, POST, OPTIONS, DELETE"); header("Access-Control-Allow-Headers:DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type, Accept-Language, Origin, Accept-Encoding,Authorization");
效果如下: