diff --git a/README.md b/README.md index 874601e..0101743 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,9 @@
+
+ * 1. 单个属性模式:
+ *
+ * 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
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * 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(), ",");
+ }
+
+}
diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/handler/BladePermissionHandler.java b/blade-core-secure/src/main/java/org/springblade/core/secure/handler/BladePermissionHandler.java
new file mode 100644
index 0000000..9cbf7e7
--- /dev/null
+++ b/blade-core-secure/src/main/java/org/springblade/core/secure/handler/BladePermissionHandler.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * 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
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * 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
-
-
-
-
-
-
@@ -39,15 +34,14 @@
|----------------------|------------|
| Java | 17+ |
| NodeJS | 18+ |
-| Spring | 6.2.11 |
-| Spring Boot | 3.5.6 |
-| Spring Cloud | 2025.0.0 |
-| Spring Cloud Alibaba | 2023.0.3.3 |
-| Nacos Alibaba | 3.1.0 |
+| Spring | 6.2.15 |
+| Spring Boot | 3.5.9 |
+| Spring Cloud | 2025.0.1 |
+| Spring Cloud Alibaba | 2025.0.0.0 |
+| Nacos Alibaba | 3.1.1 |
| Mybatis Plus | 3.5.19 |
-
## 工程结构
```
blade-tool
@@ -93,20 +87,20 @@ blade-tool
## 官方产品
-| 简介 | 演示地址 |
-|---------------|------------------------------------------------------|
-| BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) |
-| BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) |
-| BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) |
-| BladeX大模型开发平台 | [https://ai.bladex.cn](https://ai.bladex.cn) |
+| 简介 | 演示地址 |
+|-----------------|------------------------------------------------------|
+| BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) |
+| BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) |
+| BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) |
+| BladeXAI大模型平台 | [https://ai.bladex.cn/](https://ai.bladex.cn/) |
## 前端项目
-| 简介 | 地址 |
-|--------------------|----------------------------------------------------------------------------------------------------|
-| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
-| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber) |
-| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber/tree/3.x/) |
+| 简介 | 地址 |
+|--------------------|------------------------------------------------------------------------------|
+| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber) |
+| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber2](https://gitee.com/smallc/Saber/tree/vue2/) |
+| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
## 后端项目
| 简介 | 地址 |
diff --git a/blade-core-cloud/pom.xml b/blade-core-cloud/pom.xml
index b32fed5..efc4e41 100644
--- a/blade-core-cloud/pom.xml
+++ b/blade-core-cloud/pom.xml
@@ -44,7 +44,6 @@
{@code
+ * @PreAuth(permission = "user:add")
+ * @PreAuth(role = "admin")
+ * }
+ *
+ * 2. 组合属性模式:
+ * {@code
+ * @PreAuth(role = "admin", permission = "user:add")
+ * }
+ *
+ * 3. SpEL表达式模式:
+ * {@code
+ * @PreAuth("#userVO.id<10")
+ * @PreAuth("hasRole('admin')")
+ * @PreAuth("hasPermission('user:add')")
+ * @PreAuth("hasPermission(#test) and hasRole('admin')")
+ * }
+ *
+ * @author Chill
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -27,10 +51,19 @@ import java.lang.annotation.*;
public @interface PreAuth {
/**
- * Spring el
- * 文档地址:https://docs.spring.io/spring/docs/4.3.16.RELEASE/spring-framework-reference/htmlsingle/#expressions-operators-logical
+ * Spring el表达式
*/
- String value();
+ String value() default StringPool.EMPTY;
+
+ /**
+ * 接口权限编码
+ */
+ String permission() default StringPool.EMPTY;
+
+ /**
+ * 角色权限
+ */
+ String role() default StringPool.EMPTY;
}
diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/aspect/AuthAspect.java b/blade-core-secure/src/main/java/org/springblade/core/secure/aspect/AuthAspect.java
index 57ce709..9dbb30a 100644
--- a/blade-core-secure/src/main/java/org/springblade/core/secure/aspect/AuthAspect.java
+++ b/blade-core-secure/src/main/java/org/springblade/core/secure/aspect/AuthAspect.java
@@ -15,6 +15,8 @@
*/
package org.springblade.core.secure.aspect;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.auth.AuthFun;
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.utils.ClassUtil;
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.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.lang.NonNull;
import java.lang.reflect.Method;
@@ -42,13 +46,20 @@ import java.lang.reflect.Method;
*
* @author Chill
*/
+@Slf4j
@Aspect
+@RequiredArgsConstructor
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 注解
@@ -74,20 +85,30 @@ public class AuthAspect implements ApplicationContextAware {
* @param point 切点
*/
private boolean handleAuth(ProceedingJoinPoint point) {
+ // 读取权限注解,优先方法上,没有则读取类
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
- // 读取权限注解,优先方法上,没有则读取类
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();
if (StringUtil.isNotBlank(condition)) {
- Expression expression = SPEL_PARSER.parseExpression(condition);
+ Expression expression = EXPRESSION_PARSER.parseExpression(condition);
// 方法参数值
Object[] args = point.getArgs();
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) {
// 初始化Sp el表达式上下文,并设置 AuthFun
- StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
+ StandardEvaluationContext context = new StandardEvaluationContext(authFun);
// 设置表达式支持spring bean
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
for (int i = 0; i < args.length; i++) {
@@ -114,7 +135,7 @@ public class AuthAspect implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java b/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java
index 125b26f..e00bd53 100644
--- a/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java
+++ b/blade-core-secure/src/main/java/org/springblade/core/secure/auth/AuthFun.java
@@ -15,15 +15,15 @@
*/
package org.springblade.core.secure.auth;
+import jakarta.servlet.http.HttpServletRequest;
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.tool.constant.RoleConstant;
-import org.springblade.core.tool.utils.CollectionUtil;
-import org.springblade.core.tool.utils.Func;
-import org.springblade.core.tool.utils.StringUtil;
-import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.core.tool.utils.*;
-import jakarta.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
@@ -33,6 +33,37 @@ import java.util.Objects;
*/
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 {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);
}
+ /**
+ * 判断是否具有所有角色权限
+ *
+ * @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}
*/
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)) {
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);
+ }
+
}
diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java b/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java
index 3a08d3f..c1451c3 100644
--- a/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java
+++ b/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java
@@ -18,6 +18,9 @@ package org.springblade.core.secure.config;
import lombok.AllArgsConstructor;
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.SecureInterceptor;
import org.springblade.core.secure.props.BladeAuthProperties;
@@ -66,7 +69,13 @@ public class SecureConfiguration implements WebMvcConfigurer {
@Bean
public AuthAspect authAspect() {
- return new AuthAspect();
+ return new AuthAspect(new AuthFun());
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(IPermissionHandler.class)
+ public IPermissionHandler permissionHandler() {
+ return new BladePermissionHandler(jdbcTemplate);
}
@Bean
diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/constant/PermissionConstant.java b/blade-core-secure/src/main/java/org/springblade/core/secure/constant/PermissionConstant.java
new file mode 100644
index 0000000..e1d3a47
--- /dev/null
+++ b/blade-core-secure/src/main/java/org/springblade/core/secure/constant/PermissionConstant.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *