From b9bb9cecd207db46fb734145e9f1661e9e19a4b4 Mon Sep 17 00:00:00 2001 From: "udf_admin@163.com" Date: Mon, 4 May 2020 13:37:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/config/dev.env.js | 3 +- admin/config/index.js | 2 +- admin/config/prod.env.js | 3 +- admin/config/test.env.js | 3 +- admin/src/App.vue | 21 +- admin/src/api/{user.js => rbac.js} | 7 +- admin/src/components/leftMenu.vue | 57 +- admin/src/components/leftMenuItem.vue | 45 ++ admin/src/main.js | 2 +- admin/src/router/index.js | 9 +- .../org/itkk/udf/api/common/CommonUtil.java | 14 +- .../itkk/udf/api/rbac/RbacConfiguration.java | 8 + .../org/itkk/udf/api/rbac/RbacConstant.java | 47 ++ .../org/itkk/udf/api/rbac/RbacProperties.java | 72 ++ .../auth/CheckApiWebHandlerInterceptor.java | 42 ++ .../itkk/udf/api/rbac/dto/FunctionDto.java | 50 ++ .../org/itkk/udf/api/rbac/dto/MenuDto.java | 56 ++ .../org/itkk/udf/api/rbac/dto/OptionDto.java | 35 + .../org/itkk/udf/api/rbac/dto/RbacApiDto.java | 44 ++ .../udf/api/rbac/dto/RbacFunctionDto.java | 46 ++ .../api/rbac/dto/RbacFunctionOptionDto.java | 38 ++ .../itkk/udf/api/rbac/dto/RbacMenuDto.java | 59 ++ .../itkk/udf/api/rbac/dto/RbacRoleDto.java | 44 ++ .../itkk/udf/api/rbac/entity/RoleEntity.java | 68 ++ .../rbac/entity/RoleFunctionOptionEntity.java | 56 ++ .../udf/api/rbac/entity/UserRoleEntity.java | 50 ++ .../IRoleFunctionOptionRepository.java | 11 + .../api/rbac/repository/IRoleRepository.java | 11 + .../rbac/repository/IUserRoleRepository.java | 11 + .../udf/api/rbac/service/RbacService.java | 642 ++++++++++++++++++ .../itkk/udf/api/rbac/web/RbacController.java | 56 ++ .../mapper/IRoleFunctionOptionRepository.xml | 5 + .../main/resources/mapper/IRoleRepository.xml | 5 + .../resources/mapper/IUserRoleRepository.xml | 5 + .../src/main/resources/rbac/api.properties | 12 + .../main/resources/rbac/function.properties | 8 + .../src/main/resources/rbac/menu.properties | 5 + .../main/resources/rbac/role-ops.properties | 6 + .../resources/rbac/role-public.properties | 3 + 39 files changed, 1609 insertions(+), 52 deletions(-) rename admin/src/api/{user.js => rbac.js} (52%) create mode 100644 admin/src/components/leftMenuItem.vue create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/RbacProperties.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/auth/CheckApiWebHandlerInterceptor.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/FunctionDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/MenuDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/OptionDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacApiDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacFunctionOptionDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacMenuDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/dto/RbacRoleDto.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleEntity.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/RoleFunctionOptionEntity.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/entity/UserRoleEntity.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleFunctionOptionRepository.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IRoleRepository.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/repository/IUserRoleRepository.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/service/RbacService.java create mode 100644 udf-api/udf-api-rbac/src/main/java/org/itkk/udf/api/rbac/web/RbacController.java create mode 100644 udf-api/udf-api-rbac/src/main/resources/mapper/IRoleFunctionOptionRepository.xml create mode 100644 udf-api/udf-api-rbac/src/main/resources/mapper/IRoleRepository.xml create mode 100644 udf-api/udf-api-rbac/src/main/resources/mapper/IUserRoleRepository.xml create mode 100644 udf-api/udf-api-rbac/src/main/resources/rbac/api.properties create mode 100644 udf-api/udf-api-rbac/src/main/resources/rbac/function.properties create mode 100644 udf-api/udf-api-rbac/src/main/resources/rbac/menu.properties create mode 100644 udf-api/udf-api-rbac/src/main/resources/rbac/role-ops.properties create mode 100644 udf-api/udf-api-rbac/src/main/resources/rbac/role-public.properties 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 +