正文
初识ABP vNext(4):vue用户登录&菜单权限
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章。
目录
- 前言
- 开始
- 登录
- 菜单权限
- 运行测试
- 最后
前言
上一篇已经创建好了前后端项目,本篇开始编码部分。
开始
几乎所有的系统都绕不开登录功能,那么就从登录开始,完成用户登录以及用户菜单权限控制。
登录
首先用户输入账号密码点击登录,然后组合以下参数调用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 login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
clientSetting.username = username.trim()
clientSetting.password = password
login(clientSetting)
.then(response => {
const data = response
commit('SET_TOKEN', data.access_token)
setToken(data.access_token).then(() => {
resolve()
})
})
.catch(error => {
reject(error)
})
})
}, // get user info
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo()
.then(response => {
const data = response if (!data) {
reject('Verification failed, please Login again.')
} const { name } = data commit('SET_NAME', name)
commit('SET_AVATAR', '')
commit('SET_INTRODUCTION', '')
resolve(data)
})
.catch(error => {
reject(error)
})
})
}, setRoles({ commit }, roles) {
commit('SET_ROLES', roles)
}, // user logout
logout({ 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/2485
dispatch('tagsView/delAllViews', null, { root: true }) resolve()
})
})
.catch(error => {
reject(error)
})
})
}, // remove token
resetToken({ 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 sent if (store.getters.token) {
config.headers["authorization"] = "Bearer " + getToken();
}
return config;
},
(error) => {
// do something with request error
console.log(error); // for debug
return Promise.reject(error);
}
);// response interceptor
service.interceptors.response.use(
(response) => {
const res = response.data; return res;
},
(error) => {
console.log("err" + error); // for debug
Message({
message: error.message,
type: "error",
duration: 5 * 1000,
}); if (error.status === 401) {
// to re-login
MessageBox.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 bar
NProgress.start(); // set page title
document.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 page
next({ path: "/" });
NProgress.done(); // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
} else {
//user name
const name = store.getters.name; if (name) {
next();
} else {
try {
// get user info
await store.dispatch("user/getInfo"); store.dispatch("user/setRoles", abpConfig.currentUser.roles); const grantedPolicies = abpConfig.auth.grantedPolicies; // generate accessible routes map based on grantedPolicies
const accessRoutes = await store.dispatch(
"permission/generateRoutes",
grantedPolicies
); // dynamically add accessible routes
router.addRoutes(accessRoutes); // hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true });
} catch (error) {
// remove token and go to login page to re-login
await 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 directly
next();
} 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 menu
name: '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用户登录&菜单权限的更多相关文章- vue 后台管理系统菜单权限管理
来自:https://www.cnblogs.com/fqh123/p/11094296.html 侵删 login登录方法 login() { if (!this.username) { retur ...
- 学习MVC之租房网站(六)-用户登录和权限控制
在上一篇<学习MVC之租房网站(五)-权限.角色.用户管理>完成了权限.角色.用户的增删改查,现在将基于前面完成的内容,进行后台用户登录和权限控制功能的开发. 一.用户登录 用户登录涉及到 ...
- 从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程
从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程 用户登录与权限验证是网站不可缺少的一部分功能,asp.net MVC4框架内置了用于实现该功能的类库,只需要简单搭 ...
- 初识ABP vNext(3):vue对接ABP基本思路
目录 前言 开始 登录 权限 本地化 创建项目 ABP vue-element-admin 最后 前言 上一篇介绍了ABP的启动模板以及AbpHelper工具的基本使用,这一篇将进入项目实战部分.因为 ...
- 初识ABP vNext(7):vue身份认证管理&;租户管理
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 按钮级权限 身份认证管理 R/U权限 权限刷新 租户管理 租户切换 效果 最后 前言 上一篇介绍了vue+ABP国际化 ...
- 初识ABP vNext(5):ABP扩展实体
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 扩展实体 路由整理 最后 前言 上一篇实现了前端vue部分的用户登录和菜单权限控制,但是有一些问题需要解决,比如用户头 ...
- 初识ABP vNext(1):开篇计划&;基础知识
目录 前言 开始 审计(Audit) 本地化(Localization) 事件总线(Event Bus) 多租户(multi-tenancy technology) DDD分层 实体(Entity) ...
- Abp Vnext3 vue-admin-template(一用户登录)
Git地址https://github.com/PanJiaChen/vue-admin-template/blob/master/README-zh.md 官方文档https://panjiache ...
- abp vnext 开发快速入门 3 实现权限控制
上篇讲了abp vnext 实现了简单的增加操作的例子.删除更新查询基本类似,这里就不讲了,接下来说下如何实现角色权限控制. 再说之前,先说下如果想更加透彻的理解abp vnext的权限控制,最好是先 ...
随机推荐- EcmaScript相关文档
ecmascript5.1中文文档 ECMAScript 6入门 JavaScript 标准参考教程 ECMAScript 5.1简介 ES5中新增的Array方法详细说明 firefox社区java ...
- linux 上查找pid,筛选出来
ps -ef | grep httpd find / -name "1000sql.txt" 查找命令
- http请求的headers详解
关于http请求的headers详解:这里以HTTP1.1为例结合postman返回的信息 1.Server →nginx/1.15.8 A name for the server 这是post ...
- table-cell http://www.cnblogs.com/StormSpirit/archive/2012/10/24/2736453.html
http://www.cnblogs.com/StormSpirit/archive/2012/10/24/2736453.html
- np.random.rand均匀分布随机数和np.random.randn正态分布随机数函数使用方法
np.random.rand用法 觉得有用的话,欢迎一起讨论相互学习~Follow Me 生成特定形状下[0,1)下的均匀分布随机数 np.random.rand(a1,a2,a3...)生成形状为( ...
- 自定义相机下使用clippingNode注意事项
调用完clippingNode->setCameraMask(myCameraMask)后,还需要clipNode->getStencil()->setCameraMask(myCa ...
- struts2 servlet之间通信
Servlet之间通信的方式有两大类,每个类有三种不同的方法 1.request 2.session 3.application 不实现ServletContextAware,SessionAware ...
- ASP.NET SingalR + MongoDB 实现简单聊天室(一):搭建基本框架
ASP.NET SingalR不多介绍.让我介绍不如看官网,我这里就是直接上源代码,当然代码还是写的比较简单的,考虑的也少,希望各位技友多多提意见. 先简单介绍聊天室功能: 用户加入聊天室,自动给用户 ...
- vm12下Centos6安装mysql5.7
一.下载mysql的rpm tar文件 文件名称:mysql-5.7.18-1.el6.x86_64.rpm-bundle.tar官方地址:https://dev.mysql.com/get/Down ...
- Watir: 对浏览器的保存文件操作, 其实应用的是AutoIt脚本
def save_file(filepath) ai =WIN32OLE.new("AutoItX3.Control") ai.WinWait("FileDownload ...
来自:https://www.cnblogs.com/fqh123/p/11094296.html 侵删 login登录方法 login() { if (!this.username) { retur ...
在上一篇<学习MVC之租房网站(五)-权限.角色.用户管理>完成了权限.角色.用户的增删改查,现在将基于前面完成的内容,进行后台用户登录和权限控制功能的开发. 一.用户登录 用户登录涉及到 ...
从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程 用户登录与权限验证是网站不可缺少的一部分功能,asp.net MVC4框架内置了用于实现该功能的类库,只需要简单搭 ...
目录 前言 开始 登录 权限 本地化 创建项目 ABP vue-element-admin 最后 前言 上一篇介绍了ABP的启动模板以及AbpHelper工具的基本使用,这一篇将进入项目实战部分.因为 ...
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 按钮级权限 身份认证管理 R/U权限 权限刷新 租户管理 租户切换 效果 最后 前言 上一篇介绍了vue+ABP国际化 ...
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 扩展实体 路由整理 最后 前言 上一篇实现了前端vue部分的用户登录和菜单权限控制,但是有一些问题需要解决,比如用户头 ...
目录 前言 开始 审计(Audit) 本地化(Localization) 事件总线(Event Bus) 多租户(multi-tenancy technology) DDD分层 实体(Entity) ...
Git地址https://github.com/PanJiaChen/vue-admin-template/blob/master/README-zh.md 官方文档https://panjiache ...
上篇讲了abp vnext 实现了简单的增加操作的例子.删除更新查询基本类似,这里就不讲了,接下来说下如何实现角色权限控制. 再说之前,先说下如果想更加透彻的理解abp vnext的权限控制,最好是先 ...
- EcmaScript相关文档
ecmascript5.1中文文档 ECMAScript 6入门 JavaScript 标准参考教程 ECMAScript 5.1简介 ES5中新增的Array方法详细说明 firefox社区java ...
- linux 上查找pid,筛选出来
ps -ef | grep httpd find / -name "1000sql.txt" 查找命令
- http请求的headers详解
关于http请求的headers详解:这里以HTTP1.1为例结合postman返回的信息 1.Server →nginx/1.15.8 A name for the server 这是post ...
- table-cell http://www.cnblogs.com/StormSpirit/archive/2012/10/24/2736453.html
http://www.cnblogs.com/StormSpirit/archive/2012/10/24/2736453.html
- np.random.rand均匀分布随机数和np.random.randn正态分布随机数函数使用方法
np.random.rand用法 觉得有用的话,欢迎一起讨论相互学习~Follow Me 生成特定形状下[0,1)下的均匀分布随机数 np.random.rand(a1,a2,a3...)生成形状为( ...
- 自定义相机下使用clippingNode注意事项
调用完clippingNode->setCameraMask(myCameraMask)后,还需要clipNode->getStencil()->setCameraMask(myCa ...
- struts2 servlet之间通信
Servlet之间通信的方式有两大类,每个类有三种不同的方法 1.request 2.session 3.application 不实现ServletContextAware,SessionAware ...
- ASP.NET SingalR + MongoDB 实现简单聊天室(一):搭建基本框架
ASP.NET SingalR不多介绍.让我介绍不如看官网,我这里就是直接上源代码,当然代码还是写的比较简单的,考虑的也少,希望各位技友多多提意见. 先简单介绍聊天室功能: 用户加入聊天室,自动给用户 ...
- vm12下Centos6安装mysql5.7
一.下载mysql的rpm tar文件 文件名称:mysql-5.7.18-1.el6.x86_64.rpm-bundle.tar官方地址:https://dev.mysql.com/get/Down ...
- Watir: 对浏览器的保存文件操作, 其实应用的是AutoIt脚本
def save_file(filepath) ai =WIN32OLE.new("AutoItX3.Control") ai.WinWait("FileDownload ...