This commit is contained in:
udf_admin@163.com
2020-05-04 13:37:14 +08:00
parent f3befd562f
commit b9bb9cecd2
39 changed files with 1609 additions and 52 deletions
+2 -1
View File
@@ -5,5 +5,6 @@ const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
VUE_APP_TITLE: '"ITKK控制台"',
VUE_APP_BASE_URL:'"/"'
VUE_APP_BASE_URL:'"/"',
VUE_APP_ADMIN_BASE_URL:'"/admin"'
})
+1 -1
View File
@@ -50,7 +50,7 @@ module.exports = {
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
assetsPublicPath: '/admin',
/**
* Source Maps
+2 -1
View File
@@ -2,5 +2,6 @@
module.exports = {
NODE_ENV: '"production"',
VUE_APP_TITLE: '"ITKK控制台"',
VUE_APP_BASE_URL:'"/"'
VUE_APP_BASE_URL:'"/"',
VUE_APP_ADMIN_BASE_URL:'"/admin"'
}
+2 -1
View File
@@ -5,5 +5,6 @@ const devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"',
VUE_APP_TITLE: '"ITKK控制台"',
VUE_APP_BASE_URL:'"/"'
VUE_APP_BASE_URL:'"/"',
VUE_APP_ADMIN_BASE_URL:'"/admin"'
})
+15 -6
View File
@@ -6,7 +6,7 @@
@click="mainMenuBtnClick">
<i class="el-icon-menu" @click="mainMenuBtnClick" @mouseover="mainMenuBtnClick"></i>
</el-button>
<a class="mainLogoBtn" href="./" :style="mainLogoBtnStyle">
<a class="mainLogoBtn" @click="mainLogoBtnClick" :style="mainLogoBtnStyle">
<span>
<i>{{golbSetting.appLogoName}}</i>
</span>
@@ -70,7 +70,7 @@
<el-container :style="mainChildElContainerStyle">
<el-aside v-show="elAsideShow" width="240px">
<el-scrollbar style="height:100%;">
<left-menu></left-menu>
<left-menu :data="menu"></left-menu>
</el-scrollbar>
</el-aside>
<el-container :style="mainChildElContainerStyle">
@@ -105,7 +105,7 @@
direction="ltr">
<div :style="leftMenuDrawerStyle">
<el-scrollbar style="height:100%;">
<left-menu></left-menu>
<left-menu :data="menu"></left-menu>
</el-scrollbar>
</div>
</el-drawer>
@@ -114,8 +114,8 @@
<script>
import api_rbac from "./api/rbac";
import glob from "./assets/js/glob";
import api_user from "./api/user";
import leftMenu from "./components/leftMenu";
export default {
@@ -172,6 +172,7 @@
showSearchInput: false,
searchInputText: null,
userDto: {},
menu: [],
elAsideShow: true,
leftMenuDrawerShow: false,
leftMenuDrawerStyle: {}
@@ -196,10 +197,14 @@
//主动调用一次resize监听
this.resizeListener();
//检查登陆
glob
glob.checkLogin();
//获得用户信息
const userDtoResponse = await api_user.infoByToken(glob.getToken());
const userDtoResponse = await api_rbac.infoByToken(glob.getToken());
this.userDto = userDtoResponse.data.result;
//获得菜单
const menuResponse = await api_rbac.menu();
this.menu = menuResponse.data.result;
},
//重设主题属性
resetTheme() {
@@ -228,6 +233,10 @@
//判断是否显示菜单栏
this.elAsideShow = window.innerWidth > 1366 ? true : false;
},
//log点击事件
mainLogoBtnClick() {
window.location.href = `${process.env.VUE_APP_ADMIN_BASE_URL}`;
},
//搜索输入框显示和隐藏
searchInputDivShowHidden() {
this.showSearchInput = !this.showSearchInput;
@@ -235,7 +244,7 @@
//退出登录按钮事件
logoutBtnClick() {
const loading = this.$loading({lock: true, text: '操作中'});
api_user.logout().then(response => window.location.href = '/').finally(() => loading.close());
api_rbac.logout().then(response => window.location.href = '/').finally(() => loading.close());
},
//菜单按钮点击
mainMenuBtnClick() {
@@ -1,5 +1,6 @@
const api_user = {
const api_rbac = {
logout: () => axios.delete('/private/rbac/user/logout'),
infoByToken: (token = '') => axios.get('/private/rbac/user/info/by/token/' + token)
infoByToken: (token = '') => axios.get('/private/rbac/user/info/by/token/' + token),
menu: () => axios.get('/private/rbac/menu')
};
export default api_user;
export default api_rbac;
+27 -30
View File
@@ -1,43 +1,40 @@
<template>
<div class="leftMenu">
<el-menu background-color="#F5F5F5">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<el-menu background-color="#F5F5F5" @select="leftMenuSelect">
<el-menu-item index="homeabc">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
<span slot="title">首页</span>
</el-menu-item>
<div v-if="data">
<left-menu-item :data="data"></left-menu-item>
</div>
</el-menu>
</div>
</template>
<script>
import leftMenuItem from "./leftMenuItem";
export default {
name: "leftMenu"
name: "leftMenu",
components: {
leftMenuItem
},
props: {
data: {
type: Array,
required: false,
default: () => []
}
},
methods: {
//菜单激活回调
leftMenuSelect(index, indexPath) {
if (this.$route.name !== index) {
this.$router.push({name: index});
}
}
}
}
</script>
+45
View File
@@ -0,0 +1,45 @@
<template>
<div class="leftMenuItem">
<!-- 菜单 -->
<div v-for="item of data" :key="item.code">
<el-submenu :index="item.code">
<template slot="title">
<i :class="item.className"></i>
<span>{{item.name}}</span>
</template>
<!-- 子菜单 -->
<div v-if="item.childMenus">
<left-menu-item :data="item.childMenus"></left-menu-item>
</div>
<!-- 子功能 -->
<div v-if="item.childFunctions">
<div v-for="item of item.childFunctions" :key="item.code">
<el-menu-item :index="item.code">
<template slot="title">
<span>{{item.name}}</span>
</template>
</el-menu-item>
</div>
</div>
</el-submenu>
</div>
</div>
</template>
<script>
export default {
name: "leftMenuItem",
props: {
data: {
type: Array,
required: false,
default: () => []
}
}
}
</script>
<style lang="scss" scoped>
</style>
+1 -1
View File
@@ -1,7 +1,7 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import App from './App'
import router from './router'
import router from './router/index'
import "babel-polyfill";
import './assets/scss/glob.scss';
import glob from './assets/js/glob.js';
+8 -1
View File
@@ -1,9 +1,16 @@
export default new VueRouter({
mode: 'history',
base: `${process.env.VUE_APP_ADMIN_BASE_URL}`,
routes: [
{
path: '/',
name: 'home',
component: () => import('../components/home'),
},
{
path: '/a/b/c',
name: 'homeabc',
component: () => import('../components/home'),
}
]
})
});
@@ -24,11 +24,11 @@ public class CommonUtil {
* @return TokenInfoDto
*/
public static UserDto getUser(HttpServletRequest request) {
Object tokenInfoDtoObj = request.getAttribute(CommonConstant.PARAMETER_NAME_TOKEN_INFO);
if (tokenInfoDtoObj == null) {
throw new AuthException("token信息不存在");
Object userDtoObj = request.getAttribute(CommonConstant.PARAMETER_NAME_TOKEN_INFO);
if (userDtoObj == null) {
throw new AuthException("userDto信息不存在");
}
return (UserDto) tokenInfoDtoObj;
return (UserDto) userDtoObj;
}
/**
@@ -39,10 +39,10 @@ public class CommonUtil {
*/
public static String getToken(HttpServletRequest request) {
Object tokenObj = request.getAttribute(CommonConstant.PARAMETER_NAME_TOKEN);
if (tokenObj != null) {
return (String) tokenObj;
if (tokenObj == null) {
throw new AuthException("token信息不存在");
}
return null;
return (String) tokenObj;
}
/**
@@ -1,6 +1,7 @@
package org.itkk.udf.api.rbac;
import org.itkk.udf.api.common.CommonConstant;
import org.itkk.udf.api.rbac.auth.CheckApiWebHandlerInterceptor;
import org.itkk.udf.api.rbac.auth.WebHandlerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@@ -18,6 +19,12 @@ public class RbacConfiguration implements WebMvcConfigurer {
@Autowired
private WebHandlerInterceptor webHandlerInterceptor;
/**
* checkApiWebHandlerInterceptor
*/
@Autowired
private CheckApiWebHandlerInterceptor checkApiWebHandlerInterceptor;
/**
* 添加拦截器
*
@@ -26,5 +33,6 @@ public class RbacConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webHandlerInterceptor).order(1).addPathPatterns(CommonConstant.URL_ROOT_WEB_PRIVATE + "**");
registry.addInterceptor(checkApiWebHandlerInterceptor).order(2).addPathPatterns(CommonConstant.URL_ROOT_WEB_PRIVATE + "**");
}
}
@@ -10,6 +10,53 @@ public class RbacConstant {
*/
public static final String USER_KEY_PREFIX = "USER_";
/**
* rbac 角色缓存key前缀
*/
public static final String RBAC_ROLE_KEY_PREFIX = "rbac_role_";
/**
* rbac 用户缓存key前缀
*/
public static final String RBAC_USER_KEY_PREFIX = "rbac_user_";
/**
* rbac 数据缓存key前缀
*/
public static final String RBAC_CACHE_KEY_PREFIX = "rbac_";
/**
* ROOT菜单代码
*/
public static final String ROOT_MENU_CODE = "-1";
/**
* 角色状态
*/
public enum ROLE_STATUS {
/**
* 作废
*/
STATUS_F1(-1),
/**
* 启用
*/
STATUS_1(1);
private Integer value;
ROLE_STATUS(Integer value) {
this.value = value;
}
public Integer value() {
return this.value;
}
}
/**
* 用户状态(正常)
*/
@@ -0,0 +1,72 @@
package org.itkk.udf.api.rbac;
import lombok.Data;
import org.itkk.udf.api.rbac.dto.RbacApiDto;
import org.itkk.udf.api.rbac.dto.RbacFunctionDto;
import org.itkk.udf.api.rbac.dto.RbacMenuDto;
import org.itkk.udf.api.rbac.dto.RbacRoleDto;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@Component
@PropertySource(value = {
"classpath:rbac/api.properties",
"classpath:rbac/function.properties",
"classpath:rbac/menu.properties",
"classpath:rbac/role-public.properties",
"classpath:rbac/role-ops.properties"
})
@ConfigurationProperties(prefix = RbacProperties.UDF_API_RBAC_PROPERTIES)
@Validated
@Data
public class RbacProperties {
/**
* 配置文件前缀
*/
protected static final String UDF_API_RBAC_PROPERTIES = "org.itkk.udf.api.rbac";
/**
* 服务
*/
@Valid
private final Map<String, RbacApiDto> api;
/**
* 功能
*/
@Valid
private final Map<String, RbacFunctionDto> function;
/**
* 菜单
*/
@Valid
private final Map<String, RbacMenuDto> menu;
/**
* 角色 - 公共
*/
@Valid
private final Map<String, RbacRoleDto> rolePublic;
/**
* 运维人员
*/
@Valid
private final List<String> opsUsers;
/**
* 角色 - 运维
*/
@Valid
private final Map<String, RbacRoleDto> roleOps;
}
@@ -0,0 +1,42 @@
package org.itkk.udf.api.rbac.auth;
import lombok.extern.slf4j.Slf4j;
import org.itkk.udf.api.common.CommonUtil;
import org.itkk.udf.api.common.dto.UserDto;
import org.itkk.udf.api.rbac.service.RbacService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class CheckApiWebHandlerInterceptor implements HandlerInterceptor {
/**
* rbacService
*/
@Autowired
private RbacService rbacService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//排除options
if (!HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
//获得数据
String token = CommonUtil.getToken(request);
UserDto userDto = CommonUtil.getUser(request);
String uri = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString();
String method = request.getMethod();
//检查权限
rbacService.checkApi(token, userDto.getUserId(), uri, method);
}
//正常返回
return true;
}
}
@@ -0,0 +1,50 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class FunctionDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 代码
*/
private String code;
/**
* 名称
*/
@NotBlank(message = "function的name不能为空")
private String name;
/**
* 排序
*/
@NotNull(message = "function的order不能为空")
private Integer order = 1;
/**
* 子操作
*/
private List<OptionDto> options;
}
@@ -0,0 +1,56 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class MenuDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 代码
*/
private String code;
/**
* 名称
*/
private String name;
/**
* 样式名称
*/
private String className;
/**
* 排序
*/
private Integer order = 1;
/**
* 子菜单
*/
private List<MenuDto> childMenus;
/**
* 子功能
*/
private List<FunctionDto> childFunctions;
}
@@ -0,0 +1,35 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class OptionDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 代码
*/
private String code;
/**
* 名称
*/
private String name;
}
@@ -0,0 +1,44 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class RbacApiDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = 7991190522771955901L;
/**
* 名称
*/
@NotBlank(message = "api的name不能为空")
private String name;
/**
* 地址
*/
@NotBlank(message = "api的uri不能为空")
private String uri;
/**
* HTTP方法
*/
@NotBlank(message = "api的method不能为空")
private String method;
}
@@ -0,0 +1,46 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Map;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class RbacFunctionDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 名称
*/
@NotBlank(message = "function的name不能为空")
private String name;
/**
* 排序
*/
@NotNull(message = "function的order不能为空")
private Integer order = 1;
/**
* 操作
*/
@NotNull(message = "function的option不能为空")
private Map<String, RbacFunctionOptionDto> option;
}
@@ -0,0 +1,38 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class RbacFunctionOptionDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 名称
*/
@NotBlank(message = "function.option的name不能为空")
private String name;
/**
* API列表
*/
private List<String> apis;
}
@@ -0,0 +1,59 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.itkk.udf.api.rbac.RbacConstant;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class RbacMenuDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 名称
*/
@NotBlank(message = "menu的name不能为空")
private String name;
/**
* 样式名称
*/
@NotBlank(message = "menu的className不能为空")
private String className;
/**
* 父节点
*/
@NotBlank(message = "menu的parentCode不能为空")
private String parentCode = RbacConstant.ROOT_MENU_CODE;
/**
* 排序
*/
@NotNull(message = "menu的order不能为空")
private Integer order = 1;
/**
* 功能
*/
private List<String> functions;
}
@@ -0,0 +1,44 @@
package org.itkk.udf.api.rbac.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
public class RbacRoleDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 名称
*/
@NotBlank(message = "role.public的name不能为空")
private String name;
/**
* 功能 , 操作
*/
private Map<String, List<String>> function;
/**
* api
*/
private List<String> apis;
}
@@ -0,0 +1,68 @@
package org.itkk.udf.api.rbac.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("TM_RBAC_ROLE")
public class RoleEntity {
/**
* 主键
*/
@TableId("CODE")
private String code;
/**
* 名称
*/
@TableField("NAME")
private String name;
/**
* 备注
*/
@TableField("REMARK")
private String remark;
/**
* 状态 ( -1 : 作废 , 1 : 正常 ) ( 默认 : 正常 )
*/
@TableField("STATUS")
private Integer status;
/**
* 创建人
*/
@TableField("CREATE_BY")
private String createBy;
/**
* 创建时间
*/
@TableField("CREATE_DATE")
private Date createDate;
/**
* 更新人
*/
@TableField("UPDATE_BY")
private String updateBy;
/**
* 更新时间
*/
@TableField("UPDATE_DATE")
private Date updateDate;
}
@@ -0,0 +1,56 @@
package org.itkk.udf.api.rbac.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("TM_RBAC_ROLE_FUN_OPTION")
public class RoleFunctionOptionEntity {
/**
* 主键ID
*/
@TableId("ID")
private String id;
/**
* 角色代码 ( UK_1 )
*/
@TableField("ROLE_CODE")
private String roleCode;
/**
* 功能代码 ( UK_1 )
*/
@TableField("FUNCTION_CODE")
private String functionCode;
/**
* 操作代码 ( UK_1 )
*/
@TableField("OPTION_CODE")
private String optionCode;
/**
* 更新人
*/
@TableField("UPDATE_BY")
private String updateBy;
/**
* 更新时间
*/
@TableField("UPDATE_DATE")
private Date updateDate;
}
@@ -0,0 +1,50 @@
package org.itkk.udf.api.rbac.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@Accessors(chain = true)
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("TM_RBAC_USER_ROLE")
public class UserRoleEntity {
/**
* 主键ID
*/
@TableId("ID")
private String id;
/**
* 用户ID (工号) ( UK_1 )
*/
@TableField("USER_ID")
private String userId;
/**
* 角色代码 ( UK_1 )
*/
@TableField("ROLE_CODE")
private String roleCode;
/**
* 更新人
*/
@TableField("UPDATE_BY")
private String updateBy;
/**
* 更新时间
*/
@TableField("UPDATE_DATE")
private Date updateDate;
}
@@ -0,0 +1,11 @@
package org.itkk.udf.api.rbac.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.itkk.udf.api.rbac.entity.RoleFunctionOptionEntity;
@Mapper
public interface IRoleFunctionOptionRepository extends BaseMapper<RoleFunctionOptionEntity> {
}
@@ -0,0 +1,11 @@
package org.itkk.udf.api.rbac.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.itkk.udf.api.rbac.entity.RoleEntity;
@Mapper
public interface IRoleRepository extends BaseMapper<RoleEntity> {
}
@@ -0,0 +1,11 @@
package org.itkk.udf.api.rbac.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.itkk.udf.api.rbac.entity.UserRoleEntity;
@Mapper
public interface IUserRoleRepository extends BaseMapper<UserRoleEntity> {
}
@@ -0,0 +1,642 @@
package org.itkk.udf.api.rbac.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.cache.Cache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.itkk.udf.api.rbac.RbacConstant;
import org.itkk.udf.api.rbac.RbacProperties;
import org.itkk.udf.api.rbac.dto.*;
import org.itkk.udf.api.rbac.entity.RoleEntity;
import org.itkk.udf.api.rbac.entity.RoleFunctionOptionEntity;
import org.itkk.udf.api.rbac.entity.UserRoleEntity;
import org.itkk.udf.api.rbac.repository.IRoleFunctionOptionRepository;
import org.itkk.udf.api.rbac.repository.IRoleRepository;
import org.itkk.udf.api.rbac.repository.IUserRoleRepository;
import org.itkk.udf.starter.core.CoreUtil;
import org.itkk.udf.starter.core.exception.ParameterValidException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.Valid;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class RbacService {
/**
* rbacProperties
*/
@Autowired
private RbacProperties rbacProperties;
/**
* iRoleFunctionOptionRepository
*/
@Autowired
private IRoleFunctionOptionRepository iRoleFunctionOptionRepository;
/**
* iRoleRepository
*/
@Autowired
private IRoleRepository iRoleRepository;
/**
* iUserRoleRepository
*/
@Autowired
private IUserRoleRepository iUserRoleRepository;
/**
* cache
*/
@Autowired
private Cache<String, Object> cache;
/**
* 获得用户菜单
*
* @param token token
* @param userId userId
* @return List<MenuDto>
*/
public List<MenuDto> loadMenu(String token, String userId) {
return this.getAndRefReshbuildMenuFunctionOptionDtoCache(token, userId).getMenuDtoList();
}
/**
* 获得角色菜单
*
* @param roleCode roleCode
* @return List<MenuDto>
*/
public List<MenuDto> loadRoleMenu(String roleCode) {
Map<String, RbacRoleDto> rbacRoleDtoMap = new HashMap<>();
this.buildUserRoleFunctionOption(rbacRoleDtoMap, iRoleRepository.selectBatchIds(Lists.newArrayList(roleCode)));
return this.buildMenuFunctionOption(rbacRoleDtoMap).getMenuDtoList();
}
/**
* 获得所有菜单
*
* @return List<MenuDto>
*/
public List<MenuDto> loadAllMenu() {
//定义返回值
List<MenuDto> rv = new ArrayList<>();
//获得root菜单
List<Map.Entry<String, RbacMenuDto>> rootMenuList = rbacProperties.getMenu().entrySet().stream().filter(entry -> RbacConstant.ROOT_MENU_CODE.equals(entry.getValue().getParentCode())).collect(Collectors.toList());
//判空
if (CollectionUtils.isNotEmpty(rootMenuList)) {
//遍历
rootMenuList.forEach(entry -> {
//构造菜单
MenuDto menuDto = new MenuDto();
menuDto.setCode(entry.getKey());
menuDto.setName(entry.getValue().getName());
menuDto.setClassName(entry.getValue().getClassName());
menuDto.setOrder(entry.getValue().getOrder());
//递归找寻子项
loadAllMenu(menuDto);
//放入结果集
rv.add(menuDto);
});
//排序
rv.sort(Comparator.comparing(MenuDto::getOrder));
}
//返回
return rv;
}
/**
* 加载所有菜单数据
*
* @param parentMenuDto parentMenuDto
*/
private void loadAllMenu(MenuDto parentMenuDto) {
//处理子菜单
{
//获得子菜单
List<Map.Entry<String, RbacMenuDto>> menuDtoList = rbacProperties.getMenu().entrySet().stream().filter(entry -> parentMenuDto.getCode().equals(entry.getValue().getParentCode())).collect(Collectors.toList());
//判空
if (CollectionUtils.isNotEmpty(menuDtoList)) {
//初始化父菜单的子菜单集合
parentMenuDto.setChildMenus(new ArrayList<>());
//遍历
menuDtoList.forEach(entry -> {
//构造菜单
MenuDto menuDto = new MenuDto();
menuDto.setCode(entry.getKey());
menuDto.setName(entry.getValue().getName());
menuDto.setClassName(entry.getValue().getClassName());
menuDto.setOrder(entry.getValue().getOrder());
//递归找寻子项
loadAllMenu(menuDto);
//放入结果集
parentMenuDto.getChildMenus().add(menuDto);
});
//排序
parentMenuDto.getChildMenus().sort(Comparator.comparing(MenuDto::getOrder));
}
}
//处理子功能
{
//获得菜单关联的功能
List<String> functionCodeList = rbacProperties.getMenu().get(parentMenuDto.getCode()).getFunctions();
//判空
if (CollectionUtils.isNotEmpty(functionCodeList)) {
//初始化父菜单的子功能集合
parentMenuDto.setChildFunctions(new ArrayList<>());
//遍历
for (String functionCode : functionCodeList) {
//获得功能
RbacFunctionDto rbacFunctionDto = rbacProperties.getFunction().get(functionCode);
//构造功能
FunctionDto functionDto = new FunctionDto();
functionDto.setCode(functionCode);
functionDto.setName(rbacFunctionDto.getName());
functionDto.setOrder(rbacFunctionDto.getOrder());
//获得操作
if (MapUtils.isNotEmpty(rbacFunctionDto.getOption())) {
//初始化功能的操作列表
functionDto.setOptions(new ArrayList<>());
//遍历
rbacFunctionDto.getOption().forEach((key, value) -> {
//构造操作
OptionDto optionDto = new OptionDto();
optionDto.setCode(key);
optionDto.setName(value.getName());
//添加到功能中
functionDto.getOptions().add(optionDto);
});
}
//添加到菜单中
parentMenuDto.getChildFunctions().add(functionDto);
}
//去重
parentMenuDto.setChildFunctions(parentMenuDto.getChildFunctions().stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode()))), ArrayList::new)));
//排序
parentMenuDto.getChildFunctions().sort(Comparator.comparing(FunctionDto::getOrder));
}
}
}
/**
* api鉴权
*
* @param token token
* @param userId userId
* @param uri uri
* @param method method
*/
public void checkApi(String token, String userId, String uri, String method) {
//获得数据
Map<String, List<String>> api = this.loadApi(token, userId);
//判断
if (MapUtils.isEmpty(api)) {
log.warn("该用户无任何权限 : traceId -> {} , userId -> {} , URI -> {} , METHOD -> {}", CoreUtil.getTraceId(), userId, uri, method);
throw new ParameterValidException("无权限访问此接口");
}
if (!api.containsKey(uri)) {
log.warn("无URI权限 : traceId -> {} , userId -> {} , URI -> {} , METHOD -> {}", CoreUtil.getTraceId(), userId, uri, method);
throw new ParameterValidException("无权限访问此接口");
}
if (!api.get(uri).contains(method.toUpperCase())) {
log.warn("无METHOD权限 : traceId -> {} , userId -> {} , URI -> {} , METHOD -> {}", CoreUtil.getTraceId(), userId, uri, method);
throw new ParameterValidException("无权限访问此接口");
}
}
/**
* 获得用户API
*
* @param token token
* @param userId userId
* @return Map<String, List < String>>
*/
private Map<String, List<String>> loadApi(String token, String userId) {
return this.getAndRefReshbuildMenuFunctionOptionDtoCache(token, userId).getApiList();
}
/**
* 返回和刷新用户的菜单和操作的权限数据缓存
*
* @param token token
* @param userId userId
*/
private BuildMenuFunctionOptionDto getAndRefReshbuildMenuFunctionOptionDtoCache(String token, String userId) {
//构造缓存key
final String rbacCacheKey = RbacConstant.RBAC_CACHE_KEY_PREFIX + token;
//从缓存获得数据
Object cacheValue = cache.getIfPresent(rbacCacheKey);
//判空
if (cacheValue != null) {
return (BuildMenuFunctionOptionDto) cacheValue;
}
//获得数据
BuildMenuFunctionOptionDto buildMenuFunctionOptionDto = buildMenuFunctionOption(userId);
//放入缓存
cache.put(rbacCacheKey, buildMenuFunctionOptionDto);
//返回
return buildMenuFunctionOptionDto;
}
/**
* 构造用户的菜单和操作的权限数据
*
* @param userId userId
* @return BuildMenuFunctionOptionDto
*/
private BuildMenuFunctionOptionDto buildMenuFunctionOption(String userId) {
return this.buildMenuFunctionOption(this.buildUserRoleFunctionOption(userId));
}
/**
* 构造用户的菜单和操作的权限数据
*
* @param roleData roleData
* @return BuildMenuFunctionOptionDto
*/
private BuildMenuFunctionOptionDto buildMenuFunctionOption(Map<String, RbacRoleDto> roleData) {
//常量
final Map<String, RbacFunctionDto> function = new HashMap<>(this.rbacProperties.getFunction());
final Map<String, RbacMenuDto> menu = new HashMap<>(this.rbacProperties.getMenu());
final Map<String, RbacApiDto> api = new HashMap<>(this.rbacProperties.getApi());
//定义返回值
final BuildMenuFunctionOptionDto rv = new BuildMenuFunctionOptionDto();
//初始化返回值
rv.setMenuDtoList(new ArrayList<>());
rv.setApiList(new HashMap<>());
//判断role集合不为空
if (MapUtils.isNotEmpty(roleData)) {
//定义中间数据结构
final Map<String, FunctionDto> functionDtoMap = new HashMap<>();
final Map<String, MenuSimpleDto> menuSimpleDtoMap = new HashMap<>();
//构造function集合
{
//遍历role集合
roleData.values().forEach(rbacRoleDto -> {
//判断角色中的function集合不为空
if (MapUtils.isNotEmpty(rbacRoleDto.getFunction())) {
//遍历角色中的function集合
rbacRoleDto.getFunction().forEach((functionCode, optionCodeList) -> {
//判断functionCode存在基础数据中
if (function.containsKey(functionCode)) {
//获得function基础数据
final RbacFunctionDto rbacFunctionDto = function.get(functionCode);
//如果中间数据结构中存在此function,则返回,不存在,则新构造一个
final FunctionDto functionDto = Optional.ofNullable(functionDtoMap.get(functionCode)).orElse(new FunctionDto().setCode(functionCode).setName(rbacFunctionDto.getName()).setOrder(rbacFunctionDto.getOrder()));
//判断function中的optionCode集合不为空
if (CollectionUtils.isNotEmpty(optionCodeList)) {
//function中的optionCode集合(只处理存在基础数据里的optionCode)
optionCodeList.stream().filter(item -> rbacFunctionDto.getOption().containsKey(item)).forEach(optionCode -> {
//获得option基础数据
final RbacFunctionOptionDto rbacFunctionOptionDto = rbacFunctionDto.getOption().get(optionCode);
//判断functiuon中的options为空,则初始化
if (functionDto.getOptions() == null) {
functionDto.setOptions(new ArrayList<>());
}
//将option数据装入function中
functionDto.getOptions().add(new OptionDto().setCode(optionCode).setName(rbacFunctionOptionDto.getName()));
});
//将function中的option列表去重
Optional.ofNullable(functionDto.getOptions()).ifPresent(optionDtos -> functionDto.setOptions(functionDto.getOptions().stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode()))), ArrayList::new))));
}
//装入中间数据结构
functionDtoMap.put(functionCode, functionDto);
}
});
}
//判断角色中的api集合不为空
if (CollectionUtils.isNotEmpty(rbacRoleDto.getApis())) {
//构造api列表
buildApiDto(api, rv.getApiList(), rbacRoleDto.getApis());
}
});
}
//构造菜单集合
{
//遍历menu & 过滤menu中没有配置functions的节点
menu.entrySet().stream().filter(entry -> CollectionUtils.isNotEmpty(entry.getValue().getFunctions())).forEach(entry -> {
//获得menu代码
final String menuCode = entry.getKey();
//获得menu实体
final RbacMenuDto rbacMenuDto = entry.getValue();
//如果menu中间数据结构中存在此menu,则返回,不存在,则新构造一个
final MenuSimpleDto menuSimpleDto = Optional.ofNullable(menuSimpleDtoMap.get(menuCode)).orElse(new MenuSimpleDto().setCode(menuCode).setName(rbacMenuDto.getName()).setClassName(rbacMenuDto.getClassName()).setOrder(rbacMenuDto.getOrder()).setParentCode(rbacMenuDto.getParentCode()).setChildFunctions(CollectionUtils.isNotEmpty(rbacMenuDto.getFunctions()) ? new ArrayList<>() : null));
//遍历menu基础数据中的function集合,并加入中间数据结构的function集合中(只处理在function中间数据结构中存在的数据)
rbacMenuDto.getFunctions().stream().filter(item -> functionDtoMap.containsKey(item)).forEach(functionCode -> menuSimpleDto.getChildFunctions().add(functionDtoMap.get(functionCode)));
//判断menu的function是否存在数据
if (CollectionUtils.isNotEmpty(menuSimpleDto.getChildFunctions())) {
//menu中间数据结构中的function集合排序去重
menuSimpleDto.setChildFunctions(menuSimpleDto.getChildFunctions().stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode()))), ArrayList::new)).stream().sorted(Comparator.comparing(FunctionDto::getOrder)).collect(Collectors.toList()));
//装入中间数据结构
menuSimpleDtoMap.put(menuCode, menuSimpleDto);
//找寻menu父菜单
buildMenuSimpleDtoMap(menuSimpleDto.getParentCode(), menu, menuSimpleDtoMap);
}
});
}
//构造树形菜单集合
{
// 抽取menu中间数据结构的value集合 & 过滤 & 排序 & 去重
List<MenuSimpleDto> menuSimpleDtoList = menuSimpleDtoMap.values().stream()
.filter(item -> item.getParentCode().equals(RbacConstant.ROOT_MENU_CODE))
.collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode()))), ArrayList::new))
.stream()
.sorted(Comparator.comparing(MenuSimpleDto::getOrder))
.collect(Collectors.toList());
//判空
if (CollectionUtils.isNotEmpty(menuSimpleDtoList)) {
//遍历
menuSimpleDtoList.forEach(menuSimpleDto -> {
//构造menu
MenuDto item = new MenuDto().setCode(menuSimpleDto.getCode()).setName(menuSimpleDto.getName()).setClassName(menuSimpleDto.getClassName()).setOrder(menuSimpleDto.getOrder()).setChildFunctions(menuSimpleDto.getChildFunctions());
//查找menu的子节点
buildMenuDto(function, api, rv.getApiList(), item, menuSimpleDtoMap);
//填充menu
rv.getMenuDtoList().add(item);
//构造api列表
buildApiDto(function, api, rv.getApiList(), item.getChildFunctions());
});
}
}
}
//返回
return rv;
}
/**
* 构造api列表
*
* @param api api
* @param apiList apiList
* @param roleApiList roleApiList
*/
private void buildApiDto(final Map<String, RbacApiDto> api, final Map<String, List<String>> apiList, List<String> roleApiList) {
//判空
if (CollectionUtils.isNotEmpty(roleApiList)) {
//遍历
roleApiList.forEach(roleApiCole -> {
//判断
if (api.containsKey(roleApiCole)) {
//获得数据
RbacApiDto rbacApiDto = api.get(roleApiCole);
//判断uri
if (!apiList.containsKey(rbacApiDto.getUri())) {
apiList.put(rbacApiDto.getUri(), new ArrayList<>());
}
//判断method
if (!apiList.get(rbacApiDto.getUri()).contains(rbacApiDto.getMethod())) {
apiList.get(rbacApiDto.getUri()).add(rbacApiDto.getMethod().toUpperCase());
}
}
});
}
}
/**
* 构造api列表
*
* @param function function
* @param api api
* @param apiList apiList
* @param functionDtoList functionDtoList
*/
private void buildApiDto(final Map<String, RbacFunctionDto> function, final Map<String, RbacApiDto> api, final Map<String, List<String>> apiList, final List<FunctionDto> functionDtoList) {
//判空
if (CollectionUtils.isNotEmpty(functionDtoList)) {
//遍历
functionDtoList.forEach(functionDto -> {
//判空
if (CollectionUtils.isNotEmpty(functionDto.getOptions())) {
//遍历
functionDto.getOptions().forEach(optionDto -> {
//判断
if (function.containsKey(functionDto.getCode()) && function.get(functionDto.getCode()).getOption().containsKey(optionDto.getCode())) {
//获得apiCode集合
List<String> apiCodes = function.get(functionDto.getCode()).getOption().get(optionDto.getCode()).getApis();
//判空
if (CollectionUtils.isNotEmpty(apiCodes)) {
//遍历
apiCodes.forEach(apiCode -> {
//判断
if (api.containsKey(apiCode)) {
//获得数据
RbacApiDto rbacApiDto = api.get(apiCode);
//判断uri
if (!apiList.containsKey(rbacApiDto.getUri())) {
apiList.put(rbacApiDto.getUri(), new ArrayList<>());
}
//判断method
if (!apiList.get(rbacApiDto.getUri()).contains(rbacApiDto.getMethod())) {
apiList.get(rbacApiDto.getUri()).add(rbacApiDto.getMethod());
}
}
});
}
}
});
}
});
}
}
/**
* 构造菜单
*
* @param function function
* @param api api
* @param apiList apiList
* @param parentMenuDto parentMenuDto
* @param menuSimpleDtoMap menuSimpleDtoMap
*/
private void buildMenuDto(final Map<String, RbacFunctionDto> function, final Map<String, RbacApiDto> api, final Map<String, List<String>> apiList, final MenuDto parentMenuDto, final Map<String, MenuSimpleDto> menuSimpleDtoMap) {
//获得数据 过滤 & 排序 & 去重
List<MenuSimpleDto> menuSimpleDtoList = menuSimpleDtoMap.values().stream()
.filter(item -> item.getParentCode().equals(parentMenuDto.getCode()))
.collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode()))), ArrayList::new))
.stream()
.sorted(Comparator.comparing(MenuSimpleDto::getOrder))
.collect(Collectors.toList());
//判空
if (CollectionUtils.isNotEmpty(menuSimpleDtoList)) {
//实例化
parentMenuDto.setChildMenus(new ArrayList<>());
//遍历
menuSimpleDtoList.forEach(menuSimpleDto -> {
//构造
MenuDto menuDto = new MenuDto().setCode(menuSimpleDto.getCode()).setName(menuSimpleDto.getName()).setClassName(menuSimpleDto.getClassName()).setOrder(menuSimpleDto.getOrder()).setChildFunctions(menuSimpleDto.getChildFunctions());
//查找子节点
buildMenuDto(function, api, apiList, menuDto, menuSimpleDtoMap);
//填充menu
parentMenuDto.getChildMenus().add(menuDto);
//构造api列表
buildApiDto(function, api, apiList, menuDto.getChildFunctions());
});
}
}
/**
* 寻找父菜单
*
* @param menuCode menuCode
* @param menu menu
* @param menuSimpleDtoMap menuSimpleDtoMap
*/
private void buildMenuSimpleDtoMap(final String menuCode, final Map<String, RbacMenuDto> menu, final Map<String, MenuSimpleDto> menuSimpleDtoMap) {
//获得数据
RbacMenuDto rbacMenuDto = menu.get(menuCode);
//判空
if (rbacMenuDto != null) {
//装入
menuSimpleDtoMap.put(menuCode, Optional.ofNullable(menuSimpleDtoMap.get(menuCode)).orElse(new MenuSimpleDto().setCode(menuCode).setClassName(rbacMenuDto.getClassName()).setName(rbacMenuDto.getName()).setOrder(rbacMenuDto.getOrder()).setParentCode(rbacMenuDto.getParentCode()).setChildFunctions(CollectionUtils.isNotEmpty(rbacMenuDto.getFunctions()) ? new ArrayList<>() : null)));
//判断不是顶层
if (!RbacConstant.ROOT_MENU_CODE.equals(rbacMenuDto.getParentCode())) {
//递归向上
buildMenuSimpleDtoMap(rbacMenuDto.getParentCode(), menu, menuSimpleDtoMap);
}
}
}
/**
* 构造当前用户角色功能操作的数据结构
*
* @param userId userId
* @return Map<String, RbacRoleDto>
*/
private Map<String, RbacRoleDto> buildUserRoleFunctionOption(String userId) {
//定义返回值 & 获得公共角色数据
final Map<String, RbacRoleDto> rv = new HashMap<>(this.rbacProperties.getRolePublic());
//判断是运维角色,则添加运维角色
if (CollectionUtils.isNotEmpty(this.rbacProperties.getOpsUsers()) && this.rbacProperties.getOpsUsers().contains(userId)) {
rv.putAll(this.rbacProperties.getRoleOps());
}
//获得用户角色列表
List<UserRoleEntity> userRoleEntityList = iUserRoleRepository.selectList(new QueryWrapper<UserRoleEntity>().lambda().eq(UserRoleEntity::getUserId, userId));
List<RoleEntity> roleEntityList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(userRoleEntityList)) {
roleEntityList = iRoleRepository.selectList(new QueryWrapper<RoleEntity>().lambda().eq(RoleEntity::getStatus, RbacConstant.ROLE_STATUS.STATUS_1.value()).in(RoleEntity::getCode, userRoleEntityList.stream().map(UserRoleEntity::getRoleCode).distinct().collect(Collectors.toList())));
}
//构造
this.buildUserRoleFunctionOption(rv, roleEntityList);
//返回
return rv;
}
/**
* 构造当前用户角色功能操作的数据结构
*
* @param rv rv
* @param roleEntityList roleEntityList
* @return Map<String, RbacRoleDto>
*/
private void buildUserRoleFunctionOption(Map<String, RbacRoleDto> rv, List<RoleEntity> roleEntityList) {
//判空
if (CollectionUtils.isNotEmpty(roleEntityList)) {
//获得角色功能操作列表
List<RoleFunctionOptionEntity> roleFunctionOptionEntityList = iRoleFunctionOptionRepository.selectList(new QueryWrapper<RoleFunctionOptionEntity>().lambda().in(RoleFunctionOptionEntity::getRoleCode, roleEntityList.stream().map(RoleEntity::getCode).collect(Collectors.toList())));
//判空
if (CollectionUtils.isNotEmpty(roleFunctionOptionEntityList)) {
//将角色信息转换成map结构
Map<String, RoleEntity> roleMap = Maps.uniqueIndex(roleEntityList, RoleEntity::getCode);
//遍历
roleFunctionOptionEntityList.forEach(roleFunctionOptionEntity -> {
//只处理roleCode存在的情况
if (roleMap.containsKey(roleFunctionOptionEntity.getRoleCode())) {
//角色代码不在结果集
if (!rv.containsKey(roleFunctionOptionEntity.getRoleCode())) {
rv.put(roleFunctionOptionEntity.getRoleCode(), new RbacRoleDto());
rv.get(roleFunctionOptionEntity.getRoleCode()).setName(roleMap.get(roleFunctionOptionEntity.getRoleCode()).getName());
rv.get(roleFunctionOptionEntity.getRoleCode()).setFunction(new HashMap<>());
}
//功能代码不在结果集里
if (!rv.get(roleFunctionOptionEntity.getRoleCode()).getFunction().containsKey(roleFunctionOptionEntity.getFunctionCode())) {
rv.get(roleFunctionOptionEntity.getRoleCode()).getFunction().put(roleFunctionOptionEntity.getFunctionCode(), new ArrayList<>());
}
//操作代码不在结果集里
if (!rv.get(roleFunctionOptionEntity.getRoleCode()).getFunction().get(roleFunctionOptionEntity.getFunctionCode()).contains(roleFunctionOptionEntity.getOptionCode())) {
rv.get(roleFunctionOptionEntity.getRoleCode()).getFunction().get(roleFunctionOptionEntity.getFunctionCode()).add(roleFunctionOptionEntity.getOptionCode());
}
}
});
}
}
}
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
private static class BuildMenuFunctionOptionDto {
/**
* 菜单集合
*/
private List<MenuDto> menuDtoList;
/**
* API集合 ( key : uri , value : httpMethod集合 )
*/
private Map<String, List<String>> apiList;
}
@Data
@Accessors(chain = true)
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
@Valid
private static class MenuSimpleDto implements Serializable {
/**
* ID
*/
private static final long serialVersionUID = -4436379076719077791L;
/**
* 代码
*/
private String code;
/**
* 名称
*/
private String name;
/**
* 样式名称
*/
private String className;
/**
* 排序
*/
private Integer order = 1;
/**
* 父代码
*/
private String parentCode;
/**
* 子功能
*/
private List<FunctionDto> childFunctions;
}
}
@@ -0,0 +1,56 @@
package org.itkk.udf.api.rbac.web;
import org.itkk.udf.api.common.CommonConstant;
import org.itkk.udf.api.common.CommonUtil;
import org.itkk.udf.api.common.dto.UserDto;
import org.itkk.udf.api.rbac.dto.MenuDto;
import org.itkk.udf.api.rbac.service.RbacService;
import org.itkk.udf.starter.core.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
@RequestMapping(value = CommonConstant.URL_ROOT_WEB_PRIVATE + "rbac")
public class RbacController {
/**
* httpServletRequest
*/
@Autowired
private HttpServletRequest httpServletRequest;
/**
* rbacService
*/
@Autowired
private RbacService rbacService;
/**
* 获得用户菜单
*
* @return RestResponse<List < MenuDto>>
*/
@GetMapping("menu")
public RestResponse<List<MenuDto>> menu() {
String token = CommonUtil.getToken(httpServletRequest);
UserDto userDto = CommonUtil.getUser(httpServletRequest);
return new RestResponse<>(rbacService.loadMenu(token, userDto.getUserId()));
}
/**
* 获得所有菜单
*
* @return RestResponse<List < MenuDto>>
*/
@GetMapping("menu/all")
public RestResponse<List<MenuDto>> loadAllMenu() {
return new RestResponse<>(rbacService.loadAllMenu());
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.itkk.udf.api.rbac.repository.IRoleFunctionOptionRepository">
</mapper>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.itkk.udf.api.rbac.repository.IRoleFunctionOptionRepository">
</mapper>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.itkk.udf.api.rbac.repository.IRoleFunctionOptionRepository">
</mapper>
@@ -0,0 +1,12 @@
org.itkk.udf.api.rbac.api.infoByToken.name=\u83B7\u5F97\u7528\u6237\u4FE1\u606F(\u6839\u636Etoken)
org.itkk.udf.api.rbac.api.infoByToken.uri=/api/udf/web/private/rbac/user/info/by/token/{token}
org.itkk.udf.api.rbac.api.infoByToken.method=GET
#
org.itkk.udf.api.rbac.api.logout.name=\u767B\u51FA
org.itkk.udf.api.rbac.api.logout.uri=/api/udf/web/private/rbac/user/logout
org.itkk.udf.api.rbac.api.logout.method=DELETE
#
org.itkk.udf.api.rbac.api.menu.name=\u83DC\u5355
org.itkk.udf.api.rbac.api.menu.uri=/api/udf/web/private/rbac/menu
org.itkk.udf.api.rbac.api.menu.method=GET
#
@@ -0,0 +1,8 @@
org.itkk.udf.api.rbac.function.rbacRole.name=\u89D2\u8272\u7BA1\u7406
org.itkk.udf.api.rbac.function.rbacRole.order=1
org.itkk.udf.api.rbac.function.rbacRole.option.all.name=\u5168\u90E8
#
org.itkk.udf.api.rbac.function.rbacUser.name=\u7528\u6237\u7BA1\u7406
org.itkk.udf.api.rbac.function.rbacUser.order=2
org.itkk.udf.api.rbac.function.rbacUser.option.all.name=\u5168\u90E8
#
@@ -0,0 +1,5 @@
#org.itkk.udf.api.rbac.menu.rbacManager.name=\u6743\u9650\u7BA1\u7406
#org.itkk.udf.api.rbac.menu.rbacManager.className=el-icon-user-solid
#org.itkk.udf.api.rbac.menu.rbacManager.parentCode=-1
#org.itkk.udf.api.rbac.menu.rbacManager.order=1
#org.itkk.udf.api.rbac.menu.rbacManager.functions=rbacRole,rbacUser
@@ -0,0 +1,6 @@
org.itkk.udf.api.rbac.opsUsers=wangkang
#
org.itkk.udf.api.rbac.roleOps.opsRole.name=\u8FD0\u7EF4\u89D2\u8272
#org.itkk.udf.api.rbac.roleOps.opsRole.function.rbacRole=all
#org.itkk.udf.api.rbac.roleOps.opsRole.function.rbacUser=all
#
@@ -0,0 +1,3 @@
org.itkk.udf.api.rbac.rolePublic.publicRole.name=\u516C\u5171\u89D2\u8272
org.itkk.udf.api.rbac.rolePublic.publicRole.apis=infoByToken,logout,menu