Vue項目前后端分離下的前端鑒權(quán)方案

#?Vue項目前后端分離下的前端鑒權(quán)方案

###?技術(shù)棧

??前端Vue全家桶涡相,后臺.net欢峰。

###?需求分析

??1.?前端路由鑒權(quán)炊邦,屏蔽地址欄入侵

??2.?路由數(shù)據(jù)由后臺管理芙粱,前端只按固定規(guī)則異步加載路由

??3.?權(quán)限控制精確到每一個按鈕

??4.?自動更新token

??5.?同一個瀏覽器只能登錄一個賬號

###?前端方案

>?對于需求1划咐、2拴念、3,采用異步加載路由方案

??1.?首先編寫vue全局路由守衛(wèi)

??2.?排除登錄路由和無需鑒權(quán)路由

??3.?登錄后請求拉取用戶菜單數(shù)據(jù)

??4.?在vuex里處理菜單和路由匹配數(shù)據(jù)

??5.?將在vuex里處理好的路由數(shù)據(jù)通過`addRoutes`異步推入路由

??```

????router.beforeEach((to,?from,?next)?=>?{

??????//?判斷當(dāng)前用戶是否已拉取權(quán)限菜單

??????if?(store.state.sidebar.userRouter.length?===?0)?{

????????//?無菜單時拉取

????????getMenuRouter()

??????????.then(res?=>?{

????????????let?_menu?=?res.data.Data.ColumnDataList?||?[];

????????????//?if?(res.data.Data.ColumnDataList.length?>?0)?{

????????????//?整理菜單&路由數(shù)據(jù)

????????????store.commit("setMenuRouter",?_menu);

????????????//?推入權(quán)限路由列表

????????????router.addRoutes(store.state.sidebar.userRouter);

????????????next({...to,?replace:?true?});

????????????//?}

??????????})

??????????.catch(err?=>?{

????????????//?console.log(err);

????????????//?Message.error("服務(wù)器連接失敗");

??????????});

??????}?else?{

????????//當(dāng)有用戶權(quán)限的時候褐缠,說明所有可訪問路由已生成?如訪問沒權(quán)限的菜單會自動進入404頁面

????????if?(to.path?==?"/login")?{

??????????next({

????????????name:?"index"

??????????});

????????}?else?{

??????????next();

????????}

??????}

????}?else?{

??????//?無登錄狀態(tài)時重定向至登錄?或可進入無需登錄狀態(tài)路徑

??????if?(to.path?==?"/login"?||?to.meta.auth?===?0)?{

????????next();

??????}?else?{

????????next({

??????????path:?"/login"

????????});

??????}

????}

??});

??```

??#####?注意

??>?我這里無需鑒權(quán)的路由直接寫在router文件夾下的index.js政鼠,通過路由元信息meta攜帶指定標(biāo)識

??```

????{

??????path:?"/err-404",

??????name:?"err404",

??????meta:?{

?????????authentication:?false

??????},

??????component:?resolve?=>?require(["../views/error/404.vue"],?resolve)

????},

??```

??>?上面說到路由是根據(jù)后臺返回菜單數(shù)據(jù)根據(jù)一定規(guī)則生成,因此一些不是菜單队魏,又需要登錄狀態(tài)的路由公般,我寫在router文件夾下的router.js里,在上面步驟4里處理后臺返回菜單數(shù)據(jù)時器躏,和處理好的菜單路由數(shù)據(jù)合并一同通過`addRoutes`推入俐载。?

??這樣做會有一定的被地址欄入侵的風(fēng)險,但是筆者這里大多是不太重要的路由,如果你要求咳咳登失,可以定一份字典來和后臺接口配合精確加載每一個路由遏佣。

??```

??//?加入企業(yè)

??{

????path:?"/join-company",

????name:?"join-company",

????component:?resolve?=>?require([`@/views/index/join-company.vue`],?resolve)?

??},

??```

??>?在vuex中將分配的菜單數(shù)據(jù)轉(zhuǎn)化為前端可用的路由數(shù)據(jù),我是這樣做的:

??管理系統(tǒng)在新增菜單時需要填寫一個頁面地址字段`Url`,前端得到后臺菜單數(shù)據(jù)后根據(jù)`Url`字段來匹配路由加載的文件路徑揽浙,每個菜單一個文件夾的好處是:你可以在這里拆分js状婶、css和此菜單私有組件等

??```

??????menu.forEach(item?=>?{

????????????let?routerItem?=?{

??????????????path:?item.Url,

??????????????name:?item.Id,

??????????????meta:?{

????????????????auth:?item.Children,

??????????????},?//?路由元信息?定義路由時即可攜帶的參數(shù),可用來管理每個路由的按鈕操作權(quán)限

??????????????component:?resolve?=>

????????????????require([`@/views${item.Url}/index.vue`],?resolve)?//?路由映射真實視圖路徑

????????????};

????????????routerBox.push(routerItem);

????????});

??```

??>?關(guān)于如何精確控制每一個按鈕我是這樣做的馅巷,將按鈕編碼放在路由元信息里膛虫,在當(dāng)前路由下匹配來控制頁面上的按鈕是否創(chuàng)建。

??菜單數(shù)據(jù)返回的都是多級結(jié)構(gòu)钓猬,每個菜單下的子集就是當(dāng)前菜單下的按鈕權(quán)限碼數(shù)組稍刀,我把每個菜單下的按鈕放在此菜單的路由元信息`meta.auth`中。這樣作的好處是:按鈕權(quán)限校驗只需匹配每個菜單路由元信息下的數(shù)據(jù)敞曹,這樣校驗池長度通常不會超過5個账月。

??```

??created()?{

????this.owner?=?this.$route.meta.auth.map(item?=>?item.Code);

??}

??methods:?{

??????matchingOwner(auth)?{

????????return?this.owner.some(item?=>?item?===?auth);

??????}

??}


??```

??>?需求4自動更新token,就是簡單的時間判斷澳迫,并在請求頭添加字段來通知后臺更新token并在頭部返回局齿,前端接受到帶token的請求就直接更新token

??```

??//?在axios的請求攔截器中

??????let?token?=?getSession(auth_code);

??????if?(token)?config.headers.auth?=?token;

??????if?(tokenIsExpire(token))?{

????????//?判斷是否需要刷新jwt

????????config.headers.refreshtoken?=?true;

??????}

??//?在axios的響應(yīng)攔截器中

????if?(res.headers.auth)?{

??????setSession(auth_code,?res.headers.auth);

????}

??```

??>?對于需求5的處理比較麻煩,要跨tab頁只能通過`cookie`或`local`,筆者這里不允許使用`cookie`因此采用的`localstorage`橄登。通過打開的新頁面讀取`localstorage`內(nèi)的`token`數(shù)據(jù)來同步多個頁面的賬號信息抓歼。`token`使用的`jwt`并前端md5加密讥此。

??這里需要注意一點是頁面切換要立即同步賬號信息。

??>?經(jīng)過需求5改造后的全局路由守衛(wèi)是這樣的:

??```

function?_AUTH_()?{

??//?切換窗口時校驗賬號是否發(fā)生變化

??window.addEventListener("visibilitychange",?function()?{

????let?Local_auth?=?getLocal(auth_code,?true);

????let?Session_auth?=?getSession(auth_code);

????if?(document.hidden?==?false?&&?Local_auth?&&?Local_auth?!=?Session_auth)?{

??????setSession(auth_code,?Local_auth,?true);

??????router.go(0)

????}

??})

??router.beforeEach((to,?from,?next)?=>?{

??????//?判斷當(dāng)前用戶是否已拉取權(quán)限菜單

??????if?(store.state.sidebar.userRouter.length?===?0)?{

????????//?無菜單時拉取

????????getMenuRouter()

??????????.then(res?=>?{

????????????let?_menu?=?res.data.Data.ColumnDataList?||?[];

????????????//?if?(res.data.Data.ColumnDataList.length?>?0)?{

????????????//?整理菜單&路由數(shù)據(jù)

????????????store.commit("setMenuRouter",?_menu);

????????????//?推入權(quán)限路由列表

????????????router.addRoutes(store.state.sidebar.userRouter);

????????????next({...to,?replace:?true?});

????????????//?}

??????????})

??????????.catch(err?=>?{

????????????//?console.log(err);

????????????//?Message.error("服務(wù)器連接失敗");

??????????});

??????}?else?{

????????//當(dāng)有用戶權(quán)限的時候谣妻,說明所有可訪問路由已生成?如訪問沒權(quán)限的菜單會自動進入404頁面

????????if?(to.path?==?"/login")?{

??????????next({

????????????name:?"index"

??????????});

????????}?else?{

??????????next();

????????}

??????}

????}?else?{

??????//?無登錄狀態(tài)時重定向至登錄?或可進入無需登錄狀態(tài)路徑

??????if?(to.path?==?"/login"?||?to.meta.auth?===?0)?{

????????next();

??????}?else?{

????????next({

??????????path:?"/login"

????????});

??????}

????}

??});

}

```

??>?經(jīng)過需求5改造后的axios的請求攔截器是這樣的萄喳,因為ie無法使用`visibilitychange`,并且嘗試百度其他屬性無效拌禾,因此在請求發(fā)出前做了粗暴處理:

??```

??if?(ie瀏覽器)?{?

??????setLocal('_ie',?Math.random())

??????let?Local_auth?=?getLocal(auth_code,?true);

??????let?Session_auth?=?getSession(auth_code);

??????if?(Local_auth?&&?Local_auth?!=?Session_auth)?{

????????setSession(auth_code,?Local_auth,?true);

????????router.go(0)

????????return?false

??????}

????}

??```

>?這里有一個小問題需要注意:因為用的`local`因此首次打開瀏覽器可能會有登錄已過期的提示取胎,這里相信大家都能找到適合自己的處理方案

??###??結(jié)語

經(jīng)過這些簡單又好用的處理,一個基本滿足需求的前后端分離前端鑒權(quán)方案就誕生啦

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末湃窍,一起剝皮案震驚了整個濱河市闻蛀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌您市,老刑警劉巖觉痛,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異茵休,居然都是意外死亡薪棒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門榕莺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俐芯,“玉大人,你說我怎么就攤上這事钉鸯“墒罚” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵唠雕,是天一觀的道長贸营。 經(jīng)常有香客問我,道長岩睁,這世上最難降的妖魔是什么钞脂? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮捕儒,結(jié)果婚禮上冰啃,老公的妹妹穿的比我還像新娘。我一直安慰自己刘莹,他們只是感情好亿笤,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著栋猖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪汪榔。 梳的紋絲不亂的頭發(fā)上蒲拉,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天肃拜,我揣著相機與錄音,去河邊找鬼雌团。 笑死燃领,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锦援。 我是一名探鬼主播猛蔽,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灵寺!你這毒婦竟也來了曼库?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤略板,失蹤者是張志新(化名)和其女友劉穎毁枯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叮称,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡种玛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓤檐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赂韵。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挠蛉,靈堂內(nèi)的尸體忽然破棺而出祭示,到底是詐尸還是另有隱情,我是刑警寧澤碌秸,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布绍移,位于F島的核電站,受9級特大地震影響讥电,放射性物質(zhì)發(fā)生泄漏蹂窖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一恩敌、第九天 我趴在偏房一處隱蔽的房頂上張望瞬测。 院中可真熱鬧,春花似錦纠炮、人聲如沸月趟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孝宗。三九已至,卻和暖如春耕肩,著一層夾襖步出監(jiān)牢的瞬間因妇,已是汗流浹背问潭。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留婚被,地道東北人狡忙。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像址芯,于是被迫代替她去往敵國和親灾茁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容