点击上方蓝字"小黑在哪里"关注我吧

  • 登录

  • 菜单权限

  • 运行测试

前言

上一篇已经创建好了前后端项目,本篇开始编码部分。

开始

几乎所有的系统都绕不开登录功能,那么就从登录开始,完成用户登录以及用户菜单权限控制。

登录

首先用户输入账号密码点击登录,然后组合以下参数调用identityserver的/connect/token端点获取token:

{grant_type: "password",scope: "HelloAbp",username: "",password: "",client_id: "HelloAbp_App",client_secret: "1q2w3e*"
}

这个参数来自ABP模板的种子数据:

我使用的是password flow,这个flow无需重定向。如果你的网站应用只有一个的话,可以这么做,如果有多个的话建议采用其他oidc方式,把认证界面放到identityserver程序里,客户端重定向到identityserver去认证,这样其实更安全,并且你无需在每个客户端网站都做一遍登录界面和逻辑。。。

还有一点,严格来说不应该直接访问/connect/token端点获取token。首先应该从identityserver发现文档/.well-known/openid-configuration中获取配置信息,然后从/.well-known/openid-configuration/jwks端点获取公钥等信息用于校验token合法性,最后才是获取token。ABP的Angular版本就是这么做的,不过他是使用angular-oauth2-oidc这个库完成,我暂时没有找到其他的支持password flow的开源库,参考:https://github.com/IdentityModel/oidc-client-js/issues/234

前端想正常访问接口,首先需要在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);}
);

菜单权限

vue-element-admin的菜单权限是使用用户角色来控制的,我们不需要role。前面分析过,通过/api/abp/application-configuration接口的auth.grantedPolicies字段,与对应的菜单路由绑定,就可以实现权限控制了。

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 }
]

因为菜单太多了,就拿其中的一个“权限测试页”菜单举例,将它与AbpIdentity.Roles绑定测试。

运行测试

运行前后端项目,使用默认账号admin/1q2w3E*登录系统:

正常的话就可以进入这个界面了:

目前可以看到“权限测试页”菜单,因为现在还没有设置权限的界面,所以我手动去数据库把这条权限数据删除,然后测试一下:

但是手动去数据库改这个表的话会有很长一段时间的缓存,在redis中,暂时没去研究这个缓存机制,正常通过接口修改应该不会这样。。。

我手动清理了redis,运行结果如下:

最后

本篇实现了前端部分的登录和菜单权限控制,但是还有很多细节问题需要处理。比如右上角的用户头像,ABP的默认用户表中是没有头像和用户介绍字段的,下篇将完善这些问题,还有删除掉vue-element-admin多余的菜单。

如果本文对您有用,

不妨点个“”或者转发朋友圈支持一下

初识ABP vNext(4):vue用户登录菜单权限相关推荐

  1. java spring 登录验证_详解使用Spring3 实现用户登录以及权限认证

    使用Spring3 实现用户登录以及权限认证 这里我就简单介绍一下,我在实现的时候处理的一些主要的实现. 1.用户登录 用户名 密 码 © 2013 - 2014 | 以上是前台页面,后台的就是一个简 ...

  2. 初识ABP vNext(3):vue对接ABP基本思路

    点击上方蓝字"小黑在哪里"关注我吧 登录 权限 本地化 创建项目 ABP vue-element-admin 前言 上一篇介绍了ABP的启动模板以及AbpHelper工具的基本使用 ...

  3. abp angular 和mvc_初识ABP vNext(3):vue对接ABP基本思路

    登录 权限 本地化 创建项目 ABP vue-element-admin 前言 上一篇介绍了ABP的启动模板以及AbpHelper工具的基本使用,这一篇将进入项目实战部分.因为目前ABP的官方模板只支 ...

  4. 初识ABP vNext(5):ABP扩展实体

    点击上方蓝字"小黑在哪里"关注我吧 扩展实体 路由整理 前言 上一篇实现了前端vue部分的用户登录和菜单权限控制,但是有一些问题需要解决,比如用户头像.用户介绍字段目前还没有,下面 ...

  5. 初识ABP vNext(1):开篇计划基础知识

    点击上方蓝字"小黑在哪里"关注我吧 审计(Audit) 本地化(Localization) 事件总线(Event Bus) 多租户(multi-tenancy technology ...

  6. Vue保持用户登录及权限控制

    vue-router-power-demo 核心内容有两点: 一是保持用户登录状态,二是根据登录用户的角色动态挂在路由 使用vuex保持用户登录 点击登录按钮,使用vuex的actions分发登录操作 ...

  7. vue实现用户登录验证 + 权限验证 + 动态路由(左侧菜单栏)

    1. 技术栈说明 vue2.6 + vue-router + vuex + element-ui 2. 开始:新建项目 前提条件:在个人电脑上安装好nodejs(我的是14.15.1)之后,利用nod ...

  8. 【Vue.js】vue用户登录功能

    之前用vue实现一个网站的登录功能,这里做一个记录和总结,以下是需要实现的登录业务描述: 1.输入用户名和密码,点击登录按钮,若两者匹配,即可进入首页,首页展示登录用户的信息: 2.当用户登录后,无法 ...

  9. Springboot+Spring-Security+JWT 实现用户登录和权限认证

    如今,互联网项目对于安全的要求越来越严格,这就是对后端开发提出了更多的要求,目前比较成熟的几种大家比较熟悉的模式,像RBAC 基于角色权限的验证,shiro框架专门用于处理权限方面的,另一个比较流行的 ...

最新文章

  1. C++中重载赋值运算符=
  2. Python查找相同元素,不同元素
  3. 4.19. 在线用户表
  4. Page_Load的问题
  5. php网络学习,网络学习
  6. 文本居于图片左侧html,CSS实现图片与文本的居中对齐的常见方式
  7. 抖音何以为“抖音”?少不了A/B测试的功劳
  8. python培训班靠谱吗-Python培训班多少钱
  9. python图像转字符画_Python实现图片转字符画的示例
  10. SQL Server2008安装详细教程
  11. GroupByKey VS ReduceByKey
  12. win10下卸载office2010(测试多种方法后,成功实现)
  13. 【译】浏览器如何工作:在现代web浏览器场景的之下
  14. 微擎人人商城 后台开启WAP访问后 微信浏览器 跳过授权 手机号密码登录
  15. Android SystemUI 架构详解
  16. 【Unity】用Text+Image实现神奇的文字下划线
  17. 判断当前是移动端还是PC端 判断是微信浏览器还是非微信浏览器
  18. 深度解剖HashMap底层原理
  19. 微信支付apiv3全教程
  20. ArcGIS中将线转换为面

热门文章

  1. apache 静态编译和动态编译参考
  2. 求指教、。。。关于调用so文件
  3. 降低winnt Apache服务的权限,让你的虚拟主机更安全
  4. Site24x7 为Teams提供可智能 DevOps
  5. 在Ubuntu服务器上打开第二个控制台会话
  6. c++的进制转换函数
  7. Android TimeAnimator
  8. command not found Operation not permitted
  9. 辨别真假数据科学家必备手册:深度学习45个基础问题(附答案)
  10. Windows导出所有计划任务方法