mirror of
https://gitee.com/smallc/blade-tool.git
synced 2026-07-10 18:39:44 +08:00
🎉 4.8.0.RELEASE 新增接口权限模块,增强系统安全性
This commit is contained in:
parent
92770ff35e
commit
80a2844c4a
40
README.md
40
README.md
|
|
@ -1,14 +1,9 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/Release-V4.8.0-blue.svg" alt="Downloads">
|
||||||
<img src="https://img.shields.io/badge/JDK-17+-green.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/JDK-17+-green.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/Spring%20Cloud-2025-blue.svg" alt="Coverage Status">
|
<img src="https://img.shields.io/badge/Spring%20Cloud-2025-blue.svg" alt="Coverage Status">
|
||||||
<img src="https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg" alt="Downloads">
|
||||||
<a href="https://central.sonatype.com/artifact/org.springblade/blade-core-bom/versions">
|
|
||||||
<img src="https://img.shields.io/nexus/r/https/oss.sonatype.org/org.springblade/blade-core-bom.svg?style=flat-square" alt="Downloads">
|
|
||||||
</a>
|
|
||||||
<a href="https://oss.sonatype.org/content/repositories/snapshots/org/springblade/blade-core-bom/">
|
|
||||||
<img src="https://img.shields.io/nexus/s/https/oss.sonatype.org/org.springblade/blade-core-bom.svg?style=flat-square" alt="Downloads">
|
|
||||||
</a>
|
|
||||||
<br/>
|
<br/>
|
||||||
<a target="_blank" href="https://bladex.cn">
|
<a target="_blank" href="https://bladex.cn">
|
||||||
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
|
||||||
|
|
@ -39,15 +34,14 @@
|
||||||
|----------------------|------------|
|
|----------------------|------------|
|
||||||
| Java | 17+ |
|
| Java | 17+ |
|
||||||
| NodeJS | 18+ |
|
| NodeJS | 18+ |
|
||||||
| Spring | 6.2.11 |
|
| Spring | 6.2.15 |
|
||||||
| Spring Boot | 3.5.6 |
|
| Spring Boot | 3.5.9 |
|
||||||
| Spring Cloud | 2025.0.0 |
|
| Spring Cloud | 2025.0.1 |
|
||||||
| Spring Cloud Alibaba | 2023.0.3.3 |
|
| Spring Cloud Alibaba | 2025.0.0.0 |
|
||||||
| Nacos Alibaba | 3.1.0 |
|
| Nacos Alibaba | 3.1.1 |
|
||||||
| Mybatis Plus | 3.5.19 |
|
| Mybatis Plus | 3.5.19 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 工程结构
|
## 工程结构
|
||||||
```
|
```
|
||||||
blade-tool
|
blade-tool
|
||||||
|
|
@ -93,20 +87,20 @@ blade-tool
|
||||||
|
|
||||||
## 官方产品
|
## 官方产品
|
||||||
|
|
||||||
| 简介 | 演示地址 |
|
| 简介 | 演示地址 |
|
||||||
|---------------|------------------------------------------------------|
|
|-----------------|------------------------------------------------------|
|
||||||
| BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) |
|
| BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) |
|
||||||
| BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) |
|
| BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) |
|
||||||
| BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) |
|
| BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) |
|
||||||
| BladeX大模型开发平台 | [https://ai.bladex.cn](https://ai.bladex.cn) |
|
| BladeXAI大模型平台 | [https://ai.bladex.cn/](https://ai.bladex.cn/) |
|
||||||
|
|
||||||
## 前端项目
|
## 前端项目
|
||||||
|
|
||||||
| 简介 | 地址 |
|
| 简介 | 地址 |
|
||||||
|--------------------|----------------------------------------------------------------------------------------------------|
|
|--------------------|------------------------------------------------------------------------------|
|
||||||
| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
|
| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber) |
|
||||||
| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber) |
|
| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber2](https://gitee.com/smallc/Saber/tree/vue2/) |
|
||||||
| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber/tree/3.x/) |
|
| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
|
||||||
|
|
||||||
## 后端项目
|
## 后端项目
|
||||||
| 简介 | 地址 |
|
| 简介 | 地址 |
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.codecentric</groupId>
|
<groupId>de.codecentric</groupId>
|
||||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||||
<version>${spring.boot.admin.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Nacos -->
|
<!-- Nacos -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@
|
||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-core-tool</artifactId>
|
<artifactId>blade-core-tool</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
<!--Jdbc-->
|
<!--Jdbc-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,34 @@
|
||||||
*/
|
*/
|
||||||
package org.springblade.core.secure.annotation;
|
package org.springblade.core.secure.annotation;
|
||||||
|
|
||||||
|
import org.springblade.core.tool.utils.StringPool;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限注解 用于检查权限 规定访问权限
|
* 权限注解,用于检查权限,规定访问权限
|
||||||
|
* 支持以下几种使用方式:
|
||||||
|
* <p>
|
||||||
|
* 1. 单个属性模式:
|
||||||
|
* <pre>{@code
|
||||||
|
* @PreAuth(permission = "user:add")
|
||||||
|
* @PreAuth(role = "admin")
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* 2. 组合属性模式:
|
||||||
|
* <pre>{@code
|
||||||
|
* @PreAuth(role = "admin", permission = "user:add")
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* 3. SpEL表达式模式:
|
||||||
|
* <pre>{@code
|
||||||
|
* @PreAuth("#userVO.id<10")
|
||||||
|
* @PreAuth("hasRole('admin')")
|
||||||
|
* @PreAuth("hasPermission('user:add')")
|
||||||
|
* @PreAuth("hasPermission(#test) and hasRole('admin')")
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
|
@ -27,10 +51,19 @@ import java.lang.annotation.*;
|
||||||
public @interface PreAuth {
|
public @interface PreAuth {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring el
|
* Spring el表达式
|
||||||
* 文档地址:https://docs.spring.io/spring/docs/4.3.16.RELEASE/spring-framework-reference/htmlsingle/#expressions-operators-logical
|
|
||||||
*/
|
*/
|
||||||
String value();
|
String value() default StringPool.EMPTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口权限编码
|
||||||
|
*/
|
||||||
|
String permission() default StringPool.EMPTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限
|
||||||
|
*/
|
||||||
|
String role() default StringPool.EMPTY;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.springblade.core.secure.aspect;
|
package org.springblade.core.secure.aspect;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
|
@ -22,6 +24,7 @@ import org.aspectj.lang.reflect.MethodSignature;
|
||||||
import org.springblade.core.secure.annotation.PreAuth;
|
import org.springblade.core.secure.annotation.PreAuth;
|
||||||
import org.springblade.core.secure.auth.AuthFun;
|
import org.springblade.core.secure.auth.AuthFun;
|
||||||
import org.springblade.core.secure.exception.SecureException;
|
import org.springblade.core.secure.exception.SecureException;
|
||||||
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
import org.springblade.core.tool.api.ResultCode;
|
import org.springblade.core.tool.api.ResultCode;
|
||||||
import org.springblade.core.tool.utils.ClassUtil;
|
import org.springblade.core.tool.utils.ClassUtil;
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
|
@ -34,6 +37,7 @@ import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
|
@ -42,13 +46,20 @@ import java.lang.reflect.Method;
|
||||||
*
|
*
|
||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Aspect
|
@Aspect
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class AuthAspect implements ApplicationContextAware {
|
public class AuthAspect implements ApplicationContextAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限处理函数
|
||||||
|
*/
|
||||||
|
private final AuthFun authFun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表达式处理
|
* 表达式处理
|
||||||
*/
|
*/
|
||||||
private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser();
|
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切 方法 和 类上的 @PreAuth 注解
|
* 切 方法 和 类上的 @PreAuth 注解
|
||||||
|
|
@ -74,20 +85,30 @@ public class AuthAspect implements ApplicationContextAware {
|
||||||
* @param point 切点
|
* @param point 切点
|
||||||
*/
|
*/
|
||||||
private boolean handleAuth(ProceedingJoinPoint point) {
|
private boolean handleAuth(ProceedingJoinPoint point) {
|
||||||
|
// 读取权限注解,优先方法上,没有则读取类
|
||||||
MethodSignature ms = (MethodSignature) point.getSignature();
|
MethodSignature ms = (MethodSignature) point.getSignature();
|
||||||
Method method = ms.getMethod();
|
Method method = ms.getMethod();
|
||||||
// 读取权限注解,优先方法上,没有则读取类
|
|
||||||
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
|
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
|
||||||
|
// 处理接口权限属性验证
|
||||||
|
if (StringUtil.isNotBlank(preAuth.permission()) && !authFun.hasPermission(preAuth.permission())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 处理角色权限属性验证
|
||||||
|
if (StringUtil.isNotBlank(preAuth.role()) && !authFun.hasRole(preAuth.role())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// 判断表达式
|
// 判断表达式
|
||||||
String condition = preAuth.value();
|
String condition = preAuth.value();
|
||||||
if (StringUtil.isNotBlank(condition)) {
|
if (StringUtil.isNotBlank(condition)) {
|
||||||
Expression expression = SPEL_PARSER.parseExpression(condition);
|
Expression expression = EXPRESSION_PARSER.parseExpression(condition);
|
||||||
// 方法参数值
|
// 方法参数值
|
||||||
Object[] args = point.getArgs();
|
Object[] args = point.getArgs();
|
||||||
StandardEvaluationContext context = getEvaluationContext(method, args);
|
StandardEvaluationContext context = getEvaluationContext(method, args);
|
||||||
return expression.getValue(context, Boolean.class);
|
return Boolean.TRUE.equals(expression.getValue(context, Boolean.class));
|
||||||
|
} else {
|
||||||
|
// 判断用户是否通过鉴权
|
||||||
|
return AuthUtil.hasAuth();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -99,7 +120,7 @@ public class AuthAspect implements ApplicationContextAware {
|
||||||
*/
|
*/
|
||||||
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
|
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
|
||||||
// 初始化Sp el表达式上下文,并设置 AuthFun
|
// 初始化Sp el表达式上下文,并设置 AuthFun
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
|
StandardEvaluationContext context = new StandardEvaluationContext(authFun);
|
||||||
// 设置表达式支持spring bean
|
// 设置表达式支持spring bean
|
||||||
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
|
@ -114,7 +135,7 @@ public class AuthAspect implements ApplicationContextAware {
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.springblade.core.secure.auth;
|
package org.springblade.core.secure.auth;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springblade.core.launch.constant.TokenConstant;
|
import org.springblade.core.launch.constant.TokenConstant;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
|
import org.springblade.core.secure.handler.IPermissionHandler;
|
||||||
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
import org.springblade.core.secure.utils.SecureUtil;
|
import org.springblade.core.secure.utils.SecureUtil;
|
||||||
import org.springblade.core.tool.constant.RoleConstant;
|
import org.springblade.core.tool.constant.RoleConstant;
|
||||||
import org.springblade.core.tool.utils.CollectionUtil;
|
import org.springblade.core.tool.utils.*;
|
||||||
import org.springblade.core.tool.utils.Func;
|
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
|
||||||
import org.springblade.core.tool.utils.WebUtil;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,6 +33,37 @@ import java.util.Objects;
|
||||||
*/
|
*/
|
||||||
public class AuthFun {
|
public class AuthFun {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验处理器
|
||||||
|
*/
|
||||||
|
private static IPermissionHandler permissionHandler;
|
||||||
|
|
||||||
|
private static IPermissionHandler getPermissionHandler() {
|
||||||
|
if (permissionHandler == null) {
|
||||||
|
permissionHandler = SpringUtil.getBean(IPermissionHandler.class);
|
||||||
|
}
|
||||||
|
return permissionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断角色是否具有接口权限(全量校验)
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean permissionAll() {
|
||||||
|
return getPermissionHandler().permissionAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断角色是否具有接口权限
|
||||||
|
*
|
||||||
|
* @param permission 权限编号
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
return getPermissionHandler().hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 放行所有请求
|
* 放行所有请求
|
||||||
*
|
*
|
||||||
|
|
@ -51,6 +82,27 @@ public class AuthFun {
|
||||||
return hasRole(RoleConstant.ADMIN);
|
return hasRole(RoleConstant.ADMIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已授权
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasAuth() {
|
||||||
|
return AuthUtil.hasAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有时间授权
|
||||||
|
*
|
||||||
|
* @param start 开始时间
|
||||||
|
* @param end 结束时间
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasTimeAuth(Integer start, Integer end) {
|
||||||
|
Integer hour = DateUtil.hour();
|
||||||
|
return hour >= start && hour <= end;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否有该角色权限
|
* 判断是否有该角色权限
|
||||||
*
|
*
|
||||||
|
|
@ -61,6 +113,21 @@ public class AuthFun {
|
||||||
return hasAnyRole(role);
|
return hasAnyRole(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否具有所有角色权限
|
||||||
|
*
|
||||||
|
* @param role 角色集合
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasAllRole(String... role) {
|
||||||
|
for (String r : role) {
|
||||||
|
if (!hasRole(r)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否有该角色权限
|
* 判断是否有该角色权限
|
||||||
*
|
*
|
||||||
|
|
@ -68,7 +135,11 @@ public class AuthFun {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
public boolean hasAnyRole(String... role) {
|
public boolean hasAnyRole(String... role) {
|
||||||
String userRole = SecureUtil.getUser().getRoleName();
|
BladeUser user = AuthUtil.getUser();
|
||||||
|
if (user == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String userRole = user.getRoleName();
|
||||||
if (StringUtil.isBlank(userRole)) {
|
if (StringUtil.isBlank(userRole)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -94,4 +165,29 @@ public class AuthFun {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有该请求头
|
||||||
|
*
|
||||||
|
* @param header 请求头
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasHeader(String header) {
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
String value = Objects.requireNonNull(request).getHeader(header);
|
||||||
|
return StringUtil.isNotBlank(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有该请求头
|
||||||
|
*
|
||||||
|
* @param header 请求头
|
||||||
|
* @param key 请求值
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
public boolean hasHeader(String header, String key) {
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
String value = Objects.requireNonNull(request).getHeader(header);
|
||||||
|
return StringUtil.equals(value, key);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ package org.springblade.core.secure.config;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springblade.core.secure.aspect.AuthAspect;
|
import org.springblade.core.secure.aspect.AuthAspect;
|
||||||
|
import org.springblade.core.secure.auth.AuthFun;
|
||||||
|
import org.springblade.core.secure.handler.BladePermissionHandler;
|
||||||
|
import org.springblade.core.secure.handler.IPermissionHandler;
|
||||||
import org.springblade.core.secure.interceptor.ClientInterceptor;
|
import org.springblade.core.secure.interceptor.ClientInterceptor;
|
||||||
import org.springblade.core.secure.interceptor.SecureInterceptor;
|
import org.springblade.core.secure.interceptor.SecureInterceptor;
|
||||||
import org.springblade.core.secure.props.BladeAuthProperties;
|
import org.springblade.core.secure.props.BladeAuthProperties;
|
||||||
|
|
@ -66,7 +69,13 @@ public class SecureConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthAspect authAspect() {
|
public AuthAspect authAspect() {
|
||||||
return new AuthAspect();
|
return new AuthAspect(new AuthFun());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(IPermissionHandler.class)
|
||||||
|
public IPermissionHandler permissionHandler() {
|
||||||
|
return new BladePermissionHandler(jdbcTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springblade.core.secure.constant;
|
||||||
|
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验常量
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public interface PermissionConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色所有的权限编号
|
||||||
|
*
|
||||||
|
* @param size 数量
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
static String permissionAllStatement(int size) {
|
||||||
|
return StringUtil.format("select scope_path as path from blade_scope_api where id in (select scope_id from blade_role_scope where scope_category = 2 and role_id in ({}))", buildHolder(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色指定的权限编号
|
||||||
|
*
|
||||||
|
* @param size 数量
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
static String permissionCodeStatement(int size) {
|
||||||
|
return StringUtil.format("select resource_code as code from blade_scope_api where resource_code = ? and id in (select scope_id from blade_role_scope where scope_category = 2 and role_id in ({}))", buildHolder(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Sql占位符
|
||||||
|
*
|
||||||
|
* @param size 数量
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
static String buildHolder(int size) {
|
||||||
|
StringBuilder builder = StringUtil.builder().append("?,".repeat(Math.max(0, size)));
|
||||||
|
return StringUtil.removeSuffix(builder.toString(), ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springblade.core.secure.handler;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
|
import org.springblade.core.cache.utils.CacheUtil;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringPool;
|
||||||
|
import org.springblade.core.tool.utils.WebUtil;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.springblade.core.secure.constant.PermissionConstant.permissionAllStatement;
|
||||||
|
import static org.springblade.core.secure.constant.PermissionConstant.permissionCodeStatement;
|
||||||
|
import static org.springblade.core.cache.utils.CacheUtil.SYS_CACHE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认授权校验类
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class BladePermissionHandler implements IPermissionHandler {
|
||||||
|
|
||||||
|
private static final String SCOPE_CACHE_ROLE = "apiScope:role:";
|
||||||
|
|
||||||
|
private static final String SCOPE_CACHE_CODE = "apiScope:code:";
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean permissionAll() {
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
BladeUser user = AuthUtil.getUser();
|
||||||
|
if (request == null || user == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
List<String> paths = permissionPath(user.getRoleId());
|
||||||
|
if (paths.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return paths.stream().anyMatch(uri::contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
BladeUser user = AuthUtil.getUser();
|
||||||
|
if (request == null || user == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> codes = permissionCode(permission, user.getRoleId());
|
||||||
|
return !codes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取接口权限地址
|
||||||
|
*
|
||||||
|
* @param roleId 角色id
|
||||||
|
* @return permissions
|
||||||
|
*/
|
||||||
|
private List<String> permissionPath(String roleId) {
|
||||||
|
List<String> permissions = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_ROLE, roleId, List.class);
|
||||||
|
if (permissions == null) {
|
||||||
|
List<Long> roleIds = Func.toLongList(roleId);
|
||||||
|
permissions = jdbcTemplate.queryForList(permissionAllStatement(roleIds.size()), String.class, roleIds.toArray());
|
||||||
|
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_ROLE, roleId, permissions);
|
||||||
|
}
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取接口权限信息
|
||||||
|
*
|
||||||
|
* @param permission 权限编号
|
||||||
|
* @param roleId 角色id
|
||||||
|
* @return permissions
|
||||||
|
*/
|
||||||
|
private List<String> permissionCode(String permission, String roleId) {
|
||||||
|
String cacheKey = permission + StringPool.COLON + roleId;
|
||||||
|
List<String> permissions = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, cacheKey, List.class);
|
||||||
|
if (permissions == null) {
|
||||||
|
List<Object> args = new ArrayList<>(Collections.singletonList(permission));
|
||||||
|
List<Long> roleIds = Func.toLongList(roleId);
|
||||||
|
args.addAll(roleIds);
|
||||||
|
permissions = jdbcTemplate.queryForList(permissionCodeStatement(roleIds.size()), String.class, args.toArray());
|
||||||
|
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, cacheKey, permissions);
|
||||||
|
}
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springblade.core.secure.handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验通用接口
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public interface IPermissionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断角色是否具有接口权限(全量校验)
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
boolean permissionAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断角色是否具有接口权限
|
||||||
|
*
|
||||||
|
* @param permission 权限编号
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
boolean hasPermission(String permission);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -303,6 +303,15 @@ public class SecureUtil {
|
||||||
return (null == user) ? StringPool.EMPTY : user.getClientId();
|
return (null == user) ? StringPool.EMPTY : user.getClientId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已授权
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean hasAuth() {
|
||||||
|
return getUser() != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Claims
|
* 获取Claims
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
|
|
||||||
* <p>
|
|
||||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* <p>
|
|
||||||
* http://www.gnu.org/licenses/lgpl.html
|
|
||||||
* <p>
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springblade.core.tool.utils;
|
|
||||||
|
|
||||||
import org.springframework.cache.Cache;
|
|
||||||
import org.springframework.cache.CacheManager;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存工具类
|
|
||||||
*
|
|
||||||
* @author Chill
|
|
||||||
* @deprecated in favor of {org.springblade.core.cache.utils.CacheUtil}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class CacheUtil {
|
|
||||||
|
|
||||||
public static final String SYS_CACHE = "blade:sys";
|
|
||||||
|
|
||||||
private static CacheManager cacheManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存工具
|
|
||||||
*
|
|
||||||
* @return CacheManager
|
|
||||||
*/
|
|
||||||
private static CacheManager getCacheManager() {
|
|
||||||
if (cacheManager == null) {
|
|
||||||
cacheManager = SpringUtil.getBean(CacheManager.class);
|
|
||||||
}
|
|
||||||
return cacheManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存对象
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @return Cache
|
|
||||||
*/
|
|
||||||
public static Cache getCache(String cacheName) {
|
|
||||||
return getCacheManager().getCache(cacheName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @param keyPrefix 缓存键前缀
|
|
||||||
* @param key 缓存键值
|
|
||||||
* @return Cache
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Object get(String cacheName, String keyPrefix, Object key) {
|
|
||||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Cache.ValueWrapper wrapper = getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)));
|
|
||||||
return wrapper == null ? null : wrapper.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @param keyPrefix 缓存键前缀
|
|
||||||
* @param key 缓存键值
|
|
||||||
* @param type 转换类型
|
|
||||||
* @param <T> 类型
|
|
||||||
* @return Cache
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <T> T get(String cacheName, String keyPrefix, Object key, @Nullable Class<T> type) {
|
|
||||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @param keyPrefix 缓存键前缀
|
|
||||||
* @param key 缓存键值
|
|
||||||
* @param valueLoader 重载对象
|
|
||||||
* @param <T> 类型
|
|
||||||
* @return Cache
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <T> T get(String cacheName, String keyPrefix, Object key, Callable<T> valueLoader) {
|
|
||||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String fullKey = keyPrefix.concat(String.valueOf(key));
|
|
||||||
Cache cache = getCache(cacheName);
|
|
||||||
Cache.ValueWrapper valueWrapper = cache.get(fullKey);
|
|
||||||
|
|
||||||
if (valueWrapper != null) {
|
|
||||||
return (T) valueWrapper.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
T value = valueLoader.call();
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
cache.put(fullKey, value);
|
|
||||||
return value;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @param keyPrefix 缓存键前缀
|
|
||||||
* @param key 缓存键值
|
|
||||||
* @param value 缓存值
|
|
||||||
*/
|
|
||||||
public static void put(String cacheName, String keyPrefix, Object key, @Nullable Object value) {
|
|
||||||
getCache(cacheName).put(keyPrefix.concat(String.valueOf(key)), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
* @param keyPrefix 缓存键前缀
|
|
||||||
* @param key 缓存键值
|
|
||||||
*/
|
|
||||||
public static void evict(String cacheName, String keyPrefix, Object key) {
|
|
||||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getCache(cacheName).evict(keyPrefix.concat(String.valueOf(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清空缓存
|
|
||||||
*
|
|
||||||
* @param cacheName 缓存名
|
|
||||||
*/
|
|
||||||
public static void clear(String cacheName) {
|
|
||||||
if (Func.isEmpty(cacheName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getCache(cacheName).clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -597,4 +597,13 @@ public class DateUtil {
|
||||||
return format(LocalDate.now(), "yyyyMMdd");
|
return format(LocalDate.now(), "yyyyMMdd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前小时
|
||||||
|
*
|
||||||
|
* @return 当前小时 (0-23)
|
||||||
|
*/
|
||||||
|
public static Integer hour() {
|
||||||
|
return LocalDateTime.now().getHour();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,13 @@
|
||||||
package org.springblade.core.datascope.handler;
|
package org.springblade.core.datascope.handler;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.core.cache.utils.CacheUtil;
|
||||||
import org.springblade.core.datascope.constant.DataScopeConstant;
|
import org.springblade.core.datascope.constant.DataScopeConstant;
|
||||||
import org.springblade.core.datascope.model.DataScopeModel;
|
import org.springblade.core.datascope.model.DataScopeModel;
|
||||||
import org.springblade.core.tool.utils.*;
|
import org.springblade.core.tool.utils.CollectionUtil;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringPool;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
|
@ -26,7 +30,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.springblade.core.tool.utils.CacheUtil.SYS_CACHE;
|
import static org.springblade.core.cache.utils.CacheUtil.SYS_CACHE;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
13
pom.xml
13
pom.xml
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Blade-Tool Version -->
|
<!-- Blade-Tool Version -->
|
||||||
<revision>4.7.1-SNAPSHOT</revision>
|
<revision>4.8.0</revision>
|
||||||
<module.name>org.springblade.blade.tool</module.name>
|
<module.name>org.springblade.blade.tool</module.name>
|
||||||
<!-- jdk Version -->
|
<!-- jdk Version -->
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
|
|
@ -256,6 +256,17 @@
|
||||||
<artifactId>blade-starter-api-crypto</artifactId>
|
<artifactId>blade-starter-api-crypto</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Admin -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.codecentric</groupId>
|
||||||
|
<artifactId>spring-boot-admin-starter-server</artifactId>
|
||||||
|
<version>${spring.boot.admin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.codecentric</groupId>
|
||||||
|
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||||
|
<version>${spring.boot.admin.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- common -->
|
<!-- common -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user