iHRM 人力资源管理系统_第5章 权限管理与Shiro入门
学习目标:
理解前端权限控制思路
理解有状态服务和无状态服务
通过拦截器实现JWT鉴权
能够理解shiro以及shiro的认证和授权
1、前端权限控制
<1>、需求分析
(1)、需求说明
基于前后端分离的开发模式中,权限控制分为前端页面可见性权限与后端API接口可访问权限。前端的权限控制主要围绕在菜单是否可见,以及菜单中按钮是否可见两方面展开的。
(2)、实现方案
在Vue工程中,菜单可以简单的理解为Vue中的路由,只需要根据登录用户的权限信息动态的加载路由列表就可以动态的构造出访问菜单。
- 登录成功后获取用户信息,包含权限列表(菜单权限,按钮权限)
- 根据用户菜单权限列表,动态构造路由(根据路由名称和权限标识比较)
- 页面按钮权限通过自定义方法控制可见性
<2>、服务端实现
对系统微服务的FrameController的profile方法(获取用户信息接口)进行修改,添加权限信息
/**
* 获取个人信息
*/
@RequestMapping(value = "/profile", method = RequestMethod.POST)
public Result profile(HttpServletRequest request) throws Exception {
//请求中获取key为Authorization的头信息
String authorization = request.getHeader("Authorization");
if(StringUtils.isEmpty(authorization)) {
throw new CommonException(ResultCode.UNAUTHENTICATED);
}
//前后端约定头信息内容以 Bearer+空格+token 形式组成
String token = authorization.replace("Bearer ", "");
//比较并获取claims
Claims claims = jwtUtil.parseJWT(token);
if(claims == null) {
throw new CommonException(ResultCode.UNAUTHENTICATED);
}
//查询用户
User user = userService.findById(userId); ProfileResult result = null;
if("user".equals(user.getLevel())) {
result = new ProfileResult(user);
}else {
Map map = new HashMap();
if("coAdmin".equals(user.getLevel())) {
map.put("enVisible","1");
}
List<Permission> list = permissionService.findAll(map);
result = new ProfileResult(user,list);
}
return new Result(ResultCode.SUCCESS,result);
}
<3>、前端实现
(1)、路由钩子函数
vue路由提供的钩子函数(beforeEach)主要用来在加载之前拦截导航,让它完成跳转或取消。可以在路由钩子函数中进行校验是否对某个路由具有访问权限
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) {
// determine if there has token
/* has token */
if (to.path === '/login') {
next({
path: '/'})
NProgress.done() // if current page is dashboard will not trigger afterEach hook,
so manually handle it
} else {
if (store.getters.roles.length === 0) {
// 判断当前用户是否已拉取完user_info信息
store
.dispatch('GetUserInfo')
.then(res => {
// 拉取user_info
const roles = res.data.data.roles // note: roles must be a array! such as:
['editor','develop']
store.dispatch('GenerateRoutes', {
roles}).then(() => {
// 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({
...to, replace: true}) // hack方法 确保addRoutes已完成 ,set the
replace: true so the navigation will not leave a history record
})
})
.catch(() => {
store.dispatch('FedLogOut').then(() => {
Message.error('验证失败, 请重新登录')
next({
path: '/login'})
})
})
} else {
next()
}
}
} else {
/* has no token */
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next('/login') // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so
manually handle it
}
}
})
(2)、配置菜单权限
在src/module-dashboard/store/permission.js 下进行修改,开启路由配置
actions: {
GenerateRoutes({
commit }, data) {
return new Promise(resolve => {
const {
roles } = data
//动态构造权限列表
let accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
commit('SET_ROUTERS', accessedRouters)
//commit('SET_ROUTERS', asyncRouterMap) // 调试开启全部路由
resolve()
})
}
}
(3)、配置验证权限的方法
在src/utils/permission.js 配置验证是否具有权限的验证方法
import store from '@/store'
// 检查是否有权限
export function hasPermission(roles, route) {
if (roles.menus && route.name) {
return roles.menus.some(role => {
return route.name.toLowerCase() === role.toLowerCase