初识ABP vNext(4):vue用户登录菜单权限
點(diǎn)擊上方藍(lán)字"小黑在哪里"關(guān)注我吧
登錄
菜單權(quán)限
運(yùn)行測(cè)試
前言
上一篇已經(jīng)創(chuàng)建好了前后端項(xiàng)目,本篇開(kāi)始編碼部分。
開(kāi)始
幾乎所有的系統(tǒng)都繞不開(kāi)登錄功能,那么就從登錄開(kāi)始,完成用戶登錄以及用戶菜單權(quán)限控制。
登錄
首先用戶輸入賬號(hào)密碼點(diǎn)擊登錄,然后組合以下參數(shù)調(diào)用identityserver的/connect/token端點(diǎn)獲取token:
{grant_type:?"password",scope:?"HelloAbp",username:?"",password:?"",client_id:?"HelloAbp_App",client_secret:?"1q2w3e*" }這個(gè)參數(shù)來(lái)自ABP模板的種子數(shù)據(jù):
我使用的是password flow,這個(gè)flow無(wú)需重定向。如果你的網(wǎng)站應(yīng)用只有一個(gè)的話,可以這么做,如果有多個(gè)的話建議采用其他oidc方式,把認(rèn)證界面放到identityserver程序里,客戶端重定向到identityserver去認(rèn)證,這樣其實(shí)更安全,并且你無(wú)需在每個(gè)客戶端網(wǎng)站都做一遍登錄界面和邏輯。。。
還有一點(diǎn),嚴(yán)格來(lái)說(shuō)不應(yīng)該直接訪問(wèn)/connect/token端點(diǎn)獲取token。首先應(yīng)該從identityserver發(fā)現(xiàn)文檔/.well-known/openid-configuration中獲取配置信息,然后從/.well-known/openid-configuration/jwks端點(diǎn)獲取公鑰等信息用于校驗(yàn)token合法性,最后才是獲取token。ABP的Angular版本就是這么做的,不過(guò)他是使用angular-oauth2-oidc這個(gè)庫(kù)完成,我暫時(shí)沒(méi)有找到其他的支持password flow的開(kāi)源庫(kù),參考:https://github.com/IdentityModel/oidc-client-js/issues/234
前端想正常訪問(wèn)接口,首先需要在HttpApi.Host,IdentityServer增加跨域配置:
前端部分需要修改的文件太多,下面只貼出部分主要代碼,需要完整源碼的可以去GitHub拉取。
src\store\modules\user.js:
const?clientSetting?=?{grant_type:?"password",scope:?"HelloAbp",username:?"",password:?"",client_id:?"HelloAbp_App",client_secret:?"1q2w3e*" }; const?actions?=?{//?user?loginlogin({?commit?},?userInfo)?{const?{?username,?password?}?=?userInforeturn?new?Promise((resolve,?reject)?=>?{clientSetting.username?=?username.trim()clientSetting.password?=?passwordlogin(clientSetting).then(response?=>?{const?data?=?responsecommit('SET_TOKEN',?data.access_token)setToken(data.access_token).then(()?=>?{resolve()})}).catch(error?=>?{reject(error)})})},//?get?user?infogetInfo({?commit?})?{return?new?Promise((resolve,?reject)?=>?{getInfo().then(response?=>?{const?data?=?responseif?(!data)?{reject('Verification?failed,?please?Login?again.')}const?{?name?}?=?datacommit('SET_NAME',?name)commit('SET_AVATAR',?'')commit('SET_INTRODUCTION',?'')resolve(data)}).catch(error?=>?{reject(error)})})},setRoles({?commit?},?roles)?{commit('SET_ROLES',?roles)},//?user?logoutlogout({?commit,?dispatch?})?{return?new?Promise((resolve,?reject)?=>?{logout().then(()?=>?{commit('SET_TOKEN',?'')commit('SET_NAME',?'')commit('SET_AVATAR',?'')commit('SET_INTRODUCTION',?'')commit('SET_ROLES',?[])removeToken().then(()?=>?{resetRouter()//?reset?visited?views?and?cached?views//?to?fixed?https://github.com/PanJiaChen/vue-element-admin/issues/2485dispatch('tagsView/delAllViews',?null,?{?root:?true?})resolve()})}).catch(error?=>?{reject(error)})})},//?remove?tokenresetToken({?commit?})?{return?new?Promise(resolve?=>?{commit('SET_TOKEN',?'')commit('SET_NAME',?'')commit('SET_AVATAR',?'')commit('SET_INTRODUCTION',?'')commit('SET_ROLES',?[])removeToken().then(()?=>?{resolve()})})} }src\utils\auth.js:
export?async?function?setToken(token)?{const?result?=?Cookies.set(TokenKey,?token);await?store.dispatch("app/applicationConfiguration");return?result; }export?async?function?removeToken()?{const?result?=?Cookies.remove(TokenKey);await?store.dispatch("app/applicationConfiguration");return?result; }src\api\user.js:
export?function?login(data)?{return?request({baseURL:?"https://localhost:44364",url:?"/connect/token",method:?"post",headers:?{?"content-type":?"application/x-www-form-urlencoded"?},data:?qs.stringify(data),}); }export?function?getInfo()?{return?request({url:?"/api/identity/my-profile",method:?"get",}); }export?function?logout()?{return?request({baseURL:?"https://localhost:44364",url:?"/api/account/logout",method:?"get",}); }src\utils\request.js:
service.interceptors.request.use((config)?=>?{//?do?something?before?request?is?sentif?(store.getters.token)?{config.headers["authorization"]?=?"Bearer?"?+?getToken();}return?config;},(error)?=>?{//?do?something?with?request?errorconsole.log(error);?//?for?debugreturn?Promise.reject(error);} );//?response?interceptor service.interceptors.response.use((response)?=>?{const?res?=?response.data;return?res;},(error)?=>?{console.log("err"?+?error);?//?for?debugMessage({message:?error.message,type:?"error",duration:?5?*?1000,});if?(error.status?===?401)?{//?to?re-loginMessageBox.confirm("You?have?been?logged?out,?you?can?cancel?to?stay?on?this?page,?or?log?in?again","Confirm?logout",{confirmButtonText:?"Re-Login",cancelButtonText:?"Cancel",type:?"warning",}).then(()?=>?{store.dispatch("user/resetToken").then(()?=>?{location.reload();});});}return?Promise.reject(error);} );菜單權(quán)限
vue-element-admin的菜單權(quán)限是使用用戶角色來(lái)控制的,我們不需要role。前面分析過(guò),通過(guò)/api/abp/application-configuration接口的auth.grantedPolicies字段,與對(duì)應(yīng)的菜單路由綁定,就可以實(shí)現(xiàn)權(quán)限控制了。
src\permission.js:
router.beforeEach(async?(to,?from,?next)?=>?{//?start?progress?barNProgress.start();//?set?page?titledocument.title?=?getPageTitle(to.meta.title);let?abpConfig?=?store.getters.abpConfig;if?(!abpConfig)?{abpConfig?=?await?store.dispatch("app/applicationConfiguration");}if?(abpConfig.currentUser.isAuthenticated)?{if?(to.path?===?"/login")?{//?if?is?logged?in,?redirect?to?the?home?pagenext({?path:?"/"?});NProgress.done();?//?hack:?https://github.com/PanJiaChen/vue-element-admin/pull/2939}?else?{//user?nameconst?name?=?store.getters.name;if?(name)?{next();}?else?{try?{//?get?user?infoawait?store.dispatch("user/getInfo");store.dispatch("user/setRoles",?abpConfig.currentUser.roles);const?grantedPolicies?=?abpConfig.auth.grantedPolicies;//?generate?accessible?routes?map?based?on?grantedPoliciesconst?accessRoutes?=?await?store.dispatch("permission/generateRoutes",grantedPolicies);//?dynamically?add?accessible?routesrouter.addRoutes(accessRoutes);//?hack?method?to?ensure?that?addRoutes?is?complete//?set?the?replace:?true,?so?the?navigation?will?not?leave?a?history?recordnext({?...to,?replace:?true?});}?catch?(error)?{//?remove?token?and?go?to?login?page?to?re-loginawait?store.dispatch("user/resetToken");Message.error(error?||?"Has?Error");next(`/login?redirect=${to.path}`);NProgress.done();}}}}?else?{if?(whiteList.indexOf(to.path)?!==?-1)?{//?in?the?free?login?whitelist,?go?directlynext();}?else?{//?other?pages?that?do?not?have?permission?to?access?are?redirected?to?the?login?page.next(`/login?redirect=${to.path}`);NProgress.done();}} });src\store\modules\permission.js:
function?hasPermission(grantedPolicies,?route)?{if?(route.meta?&&?route.meta.policy)?{const?policy?=?route.meta.policy;return?grantedPolicies[policy];}?else?{return?true;} }export?function?filterAsyncRoutes(routes,?grantedPolicies)?{const?res?=?[];routes.forEach((route)?=>?{const?tmp?=?{?...route?};if?(hasPermission(grantedPolicies,?tmp))?{if?(tmp.children)?{tmp.children?=?filterAsyncRoutes(tmp.children,?grantedPolicies);}res.push(tmp);}});return?res; }const?state?=?{routes:?[],addRoutes:?[], };const?mutations?=?{SET_ROUTES:?(state,?routes)?=>?{state.addRoutes?=?routes;state.routes?=?constantRoutes.concat(routes);}, };const?actions?=?{generateRoutes({?commit?},?grantedPolicies)?{return?new?Promise((resolve)?=>?{let?accessedRoutes?=?filterAsyncRoutes(asyncRoutes,?grantedPolicies);commit("SET_ROUTES",?accessedRoutes);resolve(accessedRoutes);});}, };src\router\index.js:
export?const?asyncRoutes?=?[{path:?'/permission',component:?Layout,redirect:?'/permission/page',alwaysShow:?true,?//?will?always?show?the?root?menuname:?'Permission',meta:?{title:?'permission',icon:?'lock',policy:?'AbpIdentity.Roles'},children:?[{path:?'page',component:?()?=>?import('@/views/permission/page'),name:?'PagePermission',meta:?{title:?'pagePermission',policy:?'AbpIdentity.Roles'}},{path:?'directive',component:?()?=>?import('@/views/permission/directive'),name:?'DirectivePermission',meta:?{title:?'directivePermission',policy:?'AbpIdentity.Roles'}},{path:?'role',component:?()?=>?import('@/views/permission/role'),name:?'RolePermission',meta:?{title:?'rolePermission',policy:?'AbpIdentity.Roles'}}]},。。。。。。//?404?page?must?be?placed?at?the?end?!!!{?path:?'*',?redirect:?'/404',?hidden:?true?} ]因?yàn)椴藛翁嗔?#xff0c;就拿其中的一個(gè)“權(quán)限測(cè)試頁(yè)”菜單舉例,將它與AbpIdentity.Roles綁定測(cè)試。
運(yùn)行測(cè)試
運(yùn)行前后端項(xiàng)目,使用默認(rèn)賬號(hào)admin/1q2w3E*登錄系統(tǒng):
正常的話就可以進(jìn)入這個(gè)界面了:
目前可以看到“權(quán)限測(cè)試頁(yè)”菜單,因?yàn)楝F(xiàn)在還沒(méi)有設(shè)置權(quán)限的界面,所以我手動(dòng)去數(shù)據(jù)庫(kù)把這條權(quán)限數(shù)據(jù)刪除,然后測(cè)試一下:
但是手動(dòng)去數(shù)據(jù)庫(kù)改這個(gè)表的話會(huì)有很長(zhǎng)一段時(shí)間的緩存,在redis中,暫時(shí)沒(méi)去研究這個(gè)緩存機(jī)制,正常通過(guò)接口修改應(yīng)該不會(huì)這樣。。。
我手動(dòng)清理了redis,運(yùn)行結(jié)果如下:
最后
本篇實(shí)現(xiàn)了前端部分的登錄和菜單權(quán)限控制,但是還有很多細(xì)節(jié)問(wèn)題需要處理。比如右上角的用戶頭像,ABP的默認(rèn)用戶表中是沒(méi)有頭像和用戶介紹字段的,下篇將完善這些問(wèn)題,還有刪除掉vue-element-admin多余的菜單。
如果本文對(duì)您有用,
不妨點(diǎn)個(gè)“在看”或者轉(zhuǎn)發(fā)朋友圈支持一下
總結(jié)
以上是生活随笔為你收集整理的初识ABP vNext(4):vue用户登录菜单权限的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 麒麟系统兼容安卓生态 弥补生态短板
- 下一篇: html5倒计时秒杀怎么做,vue 设