From 53fbd985c89fdbbedb4c1b8475f43d5d1bc5ecd0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 23 Jan 2021 23:26:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5=20spring=20boot=20admin=20?= =?UTF-8?q?=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 16 ++++- .../web/service/PermissionService.java | 66 ------------------- .../dashboard/DashboardApplication.java | 2 + .../config/SecurityConfiguration.java | 10 +++ .../SecurityPermissionFrameworkService.java | 20 ++++++ .../web/config/WebConfiguration.java | 39 +++++------ .../impl/SysPermissionServiceImpl.java | 33 ++++++++++ src/main/resources/application.yaml | 15 +++++ 8 files changed, 109 insertions(+), 92 deletions(-) delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java diff --git a/pom.xml b/pom.xml index 56e5d9e27..b04f82bd9 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ 3.14.1 8.3.0 + 2.3.1 1.16.14 1.4.1.Final @@ -172,10 +173,10 @@ ${redisson.version} - + com.ctrip.framework.apollo - apollo-client + apollo-client 1.7.0 @@ -186,6 +187,17 @@ ${skywalking.version} + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + org.projectlombok diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java deleted file mode 100644 index cb3c2987c..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.ruoyi.framework.web.service; - -import java.util.Set; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; - -/** - * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 - * - * @author ruoyi - */ -@Service("ss") -public class PermissionService { - - /** - * 判断用户是否拥有某个角色 - * - * @param role 角色字符串 - * @return 用户是否具备某角色 - */ - public boolean hasRole(String role) { - if (StringUtils.isEmpty(role)) { - return false; - } - LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { - return false; - } - for (SysRole sysRole : loginUser.getUser().getRoles()) { - String roleKey = sysRole.getRoleKey(); - if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { - return true; - } - } - return false; - } - - /** - * 验证用户是否具有以下任意一个角色 - * - * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 - * @return 用户是否具有以下任意一个角色 - */ - public boolean hasAnyRoles(String roles) { - if (StringUtils.isEmpty(roles)) { - return false; - } - LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { - return false; - } - for (String role : roles.split(ROLE_DELIMETER)) { - if (hasRole(role)) { - return true; - } - } - return false; - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/DashboardApplication.java b/src/main/java/cn/iocoder/dashboard/DashboardApplication.java index 3161e147a..32783aa8c 100644 --- a/src/main/java/cn/iocoder/dashboard/DashboardApplication.java +++ b/src/main/java/cn/iocoder/dashboard/DashboardApplication.java @@ -1,9 +1,11 @@ package cn.iocoder.dashboard; +import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication +@EnableAdminServer public class DashboardApplication { public static void main(String[] args) { diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java index 276f05d44..2898023e2 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.framework.security.config; import cn.iocoder.dashboard.framework.security.core.filter.JwtAuthenticationTokenFilter; import cn.iocoder.dashboard.framework.security.core.handler.LogoutSuccessHandlerImpl; import cn.iocoder.dashboard.framework.web.config.WebProperties; +import de.codecentric.boot.admin.server.config.AdminServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; @@ -60,6 +61,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Resource private WebProperties webProperties; + @Resource + private AdminServerProperties adminServerProperties; /** * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 @@ -134,6 +137,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .antMatchers("/swagger-resources/**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() + // Spring Boot Admin Server 的安全配置 + .antMatchers(adminServerProperties.getContextPath()).anonymous() + .antMatchers(adminServerProperties.getContextPath() + "/**").anonymous() + // Spring Boot Actuator 的安全配置 + .antMatchers("/actuator").anonymous() + .antMatchers("/actuator/**").anonymous() + // TODO .antMatchers("/druid/**").hasAnyAuthority("druid") // TODO 芋艿,未来需要在拓展下 // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityPermissionFrameworkService.java b/src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityPermissionFrameworkService.java index debf61039..f1c0a0e92 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityPermissionFrameworkService.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityPermissionFrameworkService.java @@ -1,5 +1,7 @@ package cn.iocoder.dashboard.framework.security.core.service; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; + /** * Security 框架 Permission Service 接口,定义 security 组件需要的功能 * @@ -23,4 +25,22 @@ public interface SecurityPermissionFrameworkService { */ boolean hasAnyPermissions(String... permissions); + /** + * 判断是否有角色 + * + * 注意,角色使用的是 {@link SysRoleDO#getCode()} 标识 + * + * @param role 角色 + * @return 是否 + */ + boolean hasRole(String role); + + /** + * 判断是否有角色,任一一个即可 + * + * @param roles 角色数组 + * @return 是否 + */ + boolean hasAnyRoles(String... roles); + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java index 187b336a9..07f1468ae 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java @@ -1,14 +1,9 @@ package cn.iocoder.dashboard.framework.web.config; -import com.alibaba.fastjson.serializer.SerializerFeature; -import com.alibaba.fastjson.support.config.FastJsonConfig; -import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -17,10 +12,6 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.List; -import java.util.function.Predicate; /** * Web 配置类 @@ -41,21 +32,21 @@ public class WebConfiguration implements WebMvcConfigurer { // ========== MessageConverter 相关 ========== - @Override - public void configureMessageConverters(List> converters) { - // 创建 FastJsonHttpMessageConverter 对象 - FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); - // 自定义 FastJson 配置 - FastJsonConfig fastJsonConfig = new FastJsonConfig(); - fastJsonConfig.setCharset(Charset.defaultCharset()); // 设置字符集 - fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect, // 剔除循环引用 - SerializerFeature.WriteNonStringKeyAsString); // 解决 Integer 作为 Key 时,转换为 String 类型,避免浏览器报错 - fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); - // 设置支持的 MediaType - fastJsonHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON)); - // 添加到 converters 中 - converters.add(0, fastJsonHttpMessageConverter); // 注意,添加到最开头,放在 MappingJackson2XmlHttpMessageConverter 前面 - } +// @Override +// public void configureMessageConverters(List> converters) { +// // 创建 FastJsonHttpMessageConverter 对象 +// FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); +// // 自定义 FastJson 配置 +// FastJsonConfig fastJsonConfig = new FastJsonConfig(); +// fastJsonConfig.setCharset(Charset.defaultCharset()); // 设置字符集 +// fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect, // 剔除循环引用 +// SerializerFeature.WriteNonStringKeyAsString); // 解决 Integer 作为 Key 时,转换为 String 类型,避免浏览器报错 +// fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); +// // 设置支持的 MediaType +// fastJsonHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON)); +// // 添加到 converters 中 +// converters.add(0, fastJsonHttpMessageConverter); // 注意,添加到最开头,放在 MappingJackson2XmlHttpMessageConverter 前面 +// } // ========== Filter 相关 ========== diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java index 935efa3b4..3b3647fc4 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java @@ -19,7 +19,9 @@ import cn.iocoder.dashboard.util.collection.CollectionUtils; import cn.iocoder.dashboard.util.collection.MapUtils; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; @@ -104,6 +106,11 @@ public class SysPermissionServiceImpl implements SysPermissionService { log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size()); } + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) + public void schedulePeriodicRefresh() { + initLocalCache(); + } + /** * 如果角色与菜单的关联发生变化,从数据库中获取最新的全量角色与菜单的关联。 * 如果未发生变化,则返回空 @@ -277,4 +284,30 @@ public class SysPermissionServiceImpl implements SysPermissionService { }); } + @Override + public boolean hasRole(String role) { + return hasAnyRoles(role); + } + + @Override + public boolean hasAnyRoles(String... roles) { + // 如果为空,说明已经有权限 + if (ArrayUtil.isEmpty(roles)) { + return true; + } + + // 获得当前登陆的角色。如果为空,说明没有权限 + Set roleIds = SecurityUtils.getLoginUserRoleIds(); + if (CollUtil.isEmpty(roleIds)) { + return false; + } + // 判断是否是超管。如果是,当然符合条件 + if (roleService.hasAnyAdmin(roleIds)) { + return true; + } + Set userRoles = CollectionUtils.convertSet(roleService.listRolesFromCache(roleIds), + SysRoleDO::getCode); + return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); + } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 5c0441571..5a8d09e36 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -23,6 +23,14 @@ spring: multipart: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 + # Spring Boot Admin 配置项 + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + url: http://127.0.0.1:8080/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring # 芋道配置项,设置当前项目所有自定义的配置 yudao: @@ -70,3 +78,10 @@ mybatis-plus: logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.dashboard.modules.*.dal.mysql.dataobject + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。