diff --git a/admin/config/dev.env.js b/admin/config/dev.env.js
index 8e9eab3..9abbb04 100644
--- a/admin/config/dev.env.js
+++ b/admin/config/dev.env.js
@@ -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"'
})
diff --git a/admin/config/index.js b/admin/config/index.js
index 197c083..7a24501 100644
--- a/admin/config/index.js
+++ b/admin/config/index.js
@@ -50,7 +50,7 @@ module.exports = {
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
- assetsPublicPath: './',
+ assetsPublicPath: '/admin',
/**
* Source Maps
diff --git a/admin/config/prod.env.js b/admin/config/prod.env.js
index 3184f30..2371305 100644
--- a/admin/config/prod.env.js
+++ b/admin/config/prod.env.js
@@ -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"'
}
diff --git a/admin/config/test.env.js b/admin/config/test.env.js
index 5da7be4..3fcf220 100644
--- a/admin/config/test.env.js
+++ b/admin/config/test.env.js
@@ -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"'
})
diff --git a/admin/src/App.vue b/admin/src/App.vue
index f042f03..caa722e 100644
--- a/admin/src/App.vue
+++ b/admin/src/App.vue
@@ -6,7 +6,7 @@
@click="mainMenuBtnClick">
-
+
{{golbSetting.appLogoName}}
@@ -70,7 +70,7 @@
-
+
@@ -105,7 +105,7 @@
direction="ltr">
-
+
@@ -114,8 +114,8 @@
diff --git a/admin/src/components/leftMenuItem.vue b/admin/src/components/leftMenuItem.vue
new file mode 100644
index 0000000..1bc7f47
--- /dev/null
+++ b/admin/src/components/leftMenuItem.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
diff --git a/admin/src/main.js b/admin/src/main.js
index 2911ebc..1d88d9a 100644
--- a/admin/src/main.js
+++ b/admin/src/main.js
@@ -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';
diff --git a/admin/src/router/index.js b/admin/src/router/index.js
index a4e8804..f00057c 100644
--- a/admin/src/router/index.js
+++ b/admin/src/router/index.js
@@ -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'),
}
]
-})
+});
diff --git a/udf-api/udf-api-common/src/main/java/org/itkk/udf/api/common/CommonUtil.java b/udf-api/udf-api-common/src/main/java/org/itkk/udf/api/common/CommonUtil.java
index 4e4bfe2..7e885ce 100644
--- a/udf-api/udf-api-common/src/main/java/org/itkk/udf/api/common/CommonUtil.java
+++ b/udf-api/udf-api-common/src/main/java/org/itkk/udf/api/common/CommonUtil.java
@@ -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;
}
/**
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConfiguration.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConfiguration.java
index d90b2f1..b87a312 100644
--- a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConfiguration.java
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConfiguration.java
@@ -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 + "**");
}
}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConstant.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConstant.java
index 9f7400f..8746f64 100644
--- a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConstant.java
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacConstant.java
@@ -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;
+ }
+
+ }
+
/**
* 用户状态(正常)
*/
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacProperties.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacProperties.java
new file mode 100644
index 0000000..4bc8197
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacProperties.java
@@ -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 api;
+
+ /**
+ * 功能
+ */
+ @Valid
+ private final Map function;
+
+ /**
+ * 菜单
+ */
+ @Valid
+ private final Map menu;
+
+ /**
+ * 角色 - 公共
+ */
+ @Valid
+ private final Map rolePublic;
+
+ /**
+ * 运维人员
+ */
+ @Valid
+ private final List opsUsers;
+
+ /**
+ * 角色 - 运维
+ */
+ @Valid
+ private final Map roleOps;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/auth/CheckApiWebHandlerInterceptor.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/auth/CheckApiWebHandlerInterceptor.java
new file mode 100644
index 0000000..12ed93b
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/auth/CheckApiWebHandlerInterceptor.java
@@ -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;
+ }
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/FunctionDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/FunctionDto.java
new file mode 100644
index 0000000..7b95de2
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/FunctionDto.java
@@ -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 options;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/MenuDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/MenuDto.java
new file mode 100644
index 0000000..d21369d
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/MenuDto.java
@@ -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 childMenus;
+
+ /**
+ * 子功能
+ */
+ private List childFunctions;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/OptionDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/OptionDto.java
new file mode 100644
index 0000000..d6080ed
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/OptionDto.java
@@ -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;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacApiDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacApiDto.java
new file mode 100644
index 0000000..46ad77d
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacApiDto.java
@@ -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;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionDto.java
new file mode 100644
index 0000000..602d145
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionDto.java
@@ -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 option;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionOptionDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionOptionDto.java
new file mode 100644
index 0000000..5adc676
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionOptionDto.java
@@ -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 apis;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacMenuDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacMenuDto.java
new file mode 100644
index 0000000..68a398e
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacMenuDto.java
@@ -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 functions;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacRoleDto.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacRoleDto.java
new file mode 100644
index 0000000..39d829e
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacRoleDto.java
@@ -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> function;
+
+ /**
+ * api
+ */
+ private List apis;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleEntity.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleEntity.java
new file mode 100644
index 0000000..a0aa6b2
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleEntity.java
@@ -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;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleFunctionOptionEntity.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleFunctionOptionEntity.java
new file mode 100644
index 0000000..3d52b96
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleFunctionOptionEntity.java
@@ -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;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/UserRoleEntity.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/UserRoleEntity.java
new file mode 100644
index 0000000..3baeea8
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/UserRoleEntity.java
@@ -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;
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleFunctionOptionRepository.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleFunctionOptionRepository.java
new file mode 100644
index 0000000..60ea6e6
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleFunctionOptionRepository.java
@@ -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 {
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleRepository.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleRepository.java
new file mode 100644
index 0000000..adecc22
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleRepository.java
@@ -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 {
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IUserRoleRepository.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IUserRoleRepository.java
new file mode 100644
index 0000000..f0dd342
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IUserRoleRepository.java
@@ -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 {
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/service/RbacService.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/service/RbacService.java
new file mode 100644
index 0000000..12735f9
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/service/RbacService.java
@@ -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 cache;
+
+ /**
+ * 获得用户菜单
+ *
+ * @param token token
+ * @param userId userId
+ * @return List
+ */
+ public List loadMenu(String token, String userId) {
+ return this.getAndRefReshbuildMenuFunctionOptionDtoCache(token, userId).getMenuDtoList();
+ }
+
+ /**
+ * 获得角色菜单
+ *
+ * @param roleCode roleCode
+ * @return List
+ */
+ public List loadRoleMenu(String roleCode) {
+ Map rbacRoleDtoMap = new HashMap<>();
+ this.buildUserRoleFunctionOption(rbacRoleDtoMap, iRoleRepository.selectBatchIds(Lists.newArrayList(roleCode)));
+ return this.buildMenuFunctionOption(rbacRoleDtoMap).getMenuDtoList();
+ }
+
+ /**
+ * 获得所有菜单
+ *
+ * @return List
+ */
+ public List loadAllMenu() {
+ //定义返回值
+ List rv = new ArrayList<>();
+ //获得root菜单
+ List> 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> 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 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> 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>
+ */
+ private Map> 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 roleData) {
+ //常量
+ final Map function = new HashMap<>(this.rbacProperties.getFunction());
+ final Map menu = new HashMap<>(this.rbacProperties.getMenu());
+ final Map 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 functionDtoMap = new HashMap<>();
+ final Map 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 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 api, final Map> apiList, List 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 function, final Map api, final Map> apiList, final List 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 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 function, final Map api, final Map> apiList, final MenuDto parentMenuDto, final Map menuSimpleDtoMap) {
+ //获得数据 过滤 & 排序 & 去重
+ List 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 menu, final Map 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
+ */
+ private Map buildUserRoleFunctionOption(String userId) {
+ //定义返回值 & 获得公共角色数据
+ final Map rv = new HashMap<>(this.rbacProperties.getRolePublic());
+ //判断是运维角色,则添加运维角色
+ if (CollectionUtils.isNotEmpty(this.rbacProperties.getOpsUsers()) && this.rbacProperties.getOpsUsers().contains(userId)) {
+ rv.putAll(this.rbacProperties.getRoleOps());
+ }
+ //获得用户角色列表
+ List userRoleEntityList = iUserRoleRepository.selectList(new QueryWrapper().lambda().eq(UserRoleEntity::getUserId, userId));
+ List roleEntityList = new ArrayList<>();
+ if (CollectionUtils.isNotEmpty(userRoleEntityList)) {
+ roleEntityList = iRoleRepository.selectList(new QueryWrapper().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
+ */
+ private void buildUserRoleFunctionOption(Map rv, List roleEntityList) {
+ //判空
+ if (CollectionUtils.isNotEmpty(roleEntityList)) {
+ //获得角色功能操作列表
+ List roleFunctionOptionEntityList = iRoleFunctionOptionRepository.selectList(new QueryWrapper().lambda().in(RoleFunctionOptionEntity::getRoleCode, roleEntityList.stream().map(RoleEntity::getCode).collect(Collectors.toList())));
+ //判空
+ if (CollectionUtils.isNotEmpty(roleFunctionOptionEntityList)) {
+ //将角色信息转换成map结构
+ Map 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 menuDtoList;
+
+ /**
+ * API集合 ( key : uri , value : httpMethod集合 )
+ */
+ private Map> 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 childFunctions;
+
+ }
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/web/RbacController.java b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/web/RbacController.java
new file mode 100644
index 0000000..37d8988
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/web/RbacController.java
@@ -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>
+ */
+ @GetMapping("menu")
+ public RestResponse> menu() {
+ String token = CommonUtil.getToken(httpServletRequest);
+ UserDto userDto = CommonUtil.getUser(httpServletRequest);
+ return new RestResponse<>(rbacService.loadMenu(token, userDto.getUserId()));
+ }
+
+ /**
+ * 获得所有菜单
+ *
+ * @return RestResponse>
+ */
+ @GetMapping("menu/all")
+ public RestResponse> loadAllMenu() {
+ return new RestResponse<>(rbacService.loadAllMenu());
+ }
+
+}
diff --git a/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleFunctionOptionRepository.xml b/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleFunctionOptionRepository.xml
new file mode 100644
index 0000000..07f6dfd
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleFunctionOptionRepository.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleRepository.xml b/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleRepository.xml
new file mode 100644
index 0000000..07f6dfd
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/mapper/IRoleRepository.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/udf-api/udf-api-rbac/src/main/resources/mapper/IUserRoleRepository.xml b/udf-api/udf-api-rbac/src/main/resources/mapper/IUserRoleRepository.xml
new file mode 100644
index 0000000..07f6dfd
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/mapper/IUserRoleRepository.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/udf-api/udf-api-rbac/src/main/resources/rbac/api.properties b/udf-api/udf-api-rbac/src/main/resources/rbac/api.properties
new file mode 100644
index 0000000..e154eca
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/rbac/api.properties
@@ -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
+#
diff --git a/udf-api/udf-api-rbac/src/main/resources/rbac/function.properties b/udf-api/udf-api-rbac/src/main/resources/rbac/function.properties
new file mode 100644
index 0000000..89c850b
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/rbac/function.properties
@@ -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
+#
diff --git a/udf-api/udf-api-rbac/src/main/resources/rbac/menu.properties b/udf-api/udf-api-rbac/src/main/resources/rbac/menu.properties
new file mode 100644
index 0000000..defbd41
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/rbac/menu.properties
@@ -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
diff --git a/udf-api/udf-api-rbac/src/main/resources/rbac/role-ops.properties b/udf-api/udf-api-rbac/src/main/resources/rbac/role-ops.properties
new file mode 100644
index 0000000..4c65f3b
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/rbac/role-ops.properties
@@ -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
+#
diff --git a/udf-api/udf-api-rbac/src/main/resources/rbac/role-public.properties b/udf-api/udf-api-rbac/src/main/resources/rbac/role-public.properties
new file mode 100644
index 0000000..1673e48
--- /dev/null
+++ b/udf-api/udf-api-rbac/src/main/resources/rbac/role-public.properties
@@ -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
+