org.apache.commons
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/more/SaCheckELController.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/more/SaCheckELController.java
new file mode 100644
index 00000000..b0aa130f
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/more/SaCheckELController.java
@@ -0,0 +1,102 @@
+package com.pj.cases.more;
+
+import cn.dev33.satoken.annotation.SaCheckEL;
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.util.SaResult;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * SaCheckEL EL表达式注解鉴权示例
+ *
+ * @author click33
+ * @since 2022-10-13
+ */
+@RestController
+@RequestMapping("/check-el/")
+public class SaCheckELController {
+
+ // 登录校验 ---- http://localhost:8081/check-el/test1
+ @SaCheckEL("stp.checkLogin()")
+ @RequestMapping("test1")
+ public SaResult test1() {
+ return SaResult.ok();
+ }
+
+ // 角色校验 ---- http://localhost:8081/check-el/test2
+ @SaCheckEL("stp.checkRole('dev-admin')")
+ @RequestMapping("test2")
+ public SaResult test2() {
+ return SaResult.ok();
+ }
+
+ // 权限校验 ---- http://localhost:8081/check-el/test3
+ @SaCheckEL("stp.checkPermission('user:edit')")
+ @RequestMapping("test3")
+ public SaResult test3() {
+ return SaResult.ok();
+ }
+
+ // 二级认证 ---- http://localhost:8081/check-el/test4
+ @SaCheckEL("stp.checkSafe()")
+ @RequestMapping("test4")
+ public SaResult test4() {
+ return SaResult.ok();
+ }
+
+ // 参数长度校验 ---- http://localhost:8081/check-el/test5?name=zhangsan
+ @SaCheckEL("NEED( #name.length() > 3 )")
+ @RequestMapping("test5")
+ public SaResult test5(@RequestParam(defaultValue = "") String name) {
+ return SaResult.ok().set("name", name);
+ }
+
+ // 参数长度校验,并自定义异常描述信息 ---- http://localhost:8081/check-el/test6?name=z
+ @SaCheckEL("NEED( #name !=null && #name.length() > 3, 'name长度不够' )")
+ @RequestMapping("test6")
+ public SaResult test6(String name) {
+ return SaResult.ok().set("name", name);
+ }
+
+ // 已登录, 或者查询数据在公开范围内 ---- http://localhost:8081/check-el/test7?id=10044
+ @SaCheckEL("NEED( stp.isLogin() or (#id != null and #id > 10010) )")
+ @RequestMapping("test7")
+ public SaResult test7(long id) {
+ return SaResult.ok().set("id", id);
+ }
+
+ // SaSession 里取值校验 ---- http://localhost:8081/check-el/test8
+ @SaCheckEL("NEED( stp.getSession().get('name') == 'zhangsan' )")
+ @RequestMapping("test8")
+ public SaResult test8() {
+ return SaResult.ok();
+ }
+
+ // 多账号体系鉴权测试 ---- http://localhost:8081/check-el/test9
+ @SaCheckEL("stpUser.checkLogin()")
+ @RequestMapping("test9")
+ public SaResult test9() {
+ return SaResult.ok();
+ }
+
+ // 本模块需要鉴权的权限码
+ public String permissionCode = "article:add";
+
+ // 调用本类的成员变量 ---- http://localhost:8081/check-el/test10
+ @SaCheckEL("stp.checkPermission( this.permissionCode )")
+ @RequestMapping("test10")
+ public SaResult test10() {
+ return SaResult.ok();
+ }
+
+ // 忽略鉴权测试 ---- http://localhost:8081/check-el/test11
+ @SaIgnore
+ @SaCheckEL("stp.checkPermission( 'abc' )")
+ @RequestMapping("test11")
+ public SaResult test11() {
+ return SaResult.ok();
+ }
+
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
index 76be8691..1b8be8e0 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
@@ -120,6 +120,15 @@ public class SaTokenConfigure implements WebMvcConfigurer {
SaAnnotationStrategy.instance.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
+
+ // 重写 SaCheckELRootMap 扩展函数,增加注解鉴权 EL 表达式可使用的根对象
+ SaAnnotationStrategy.instance.checkELRootMapExtendFunction = rootMap -> {
+ System.out.println("--------- 执行 SaCheckELRootMap 增强,目前已包含的的跟对象包括:" + rootMap.keySet());
+ // 新增 stpUser 根对象,使之可以在表达式中通过 stpUser.checkLogin() 方式进行多账号体系鉴权
+ rootMap.put("stpUser", StpUserUtil.getStpLogic());
+ };
}
-
+
+
+
}
diff --git a/sa-token-doc/_sidebar.md b/sa-token-doc/_sidebar.md
index 0865c3ca..3970e111 100644
--- a/sa-token-doc/_sidebar.md
+++ b/sa-token-doc/_sidebar.md
@@ -88,6 +88,7 @@
- [持久层扩展](/plugin/dao-extend)
- [和 Thymeleaf 集成](/plugin/thymeleaf-extend)
- [和 Freemarker 集成](/plugin/freemarker-extend)
+ - [注解鉴权 SpEL 表达式](/plugin/spel-at)
- [和 jwt 集成](/plugin/jwt-extend)
- [和 Dubbo 集成](/plugin/dubbo-extend)
- [和 gRPC 集成](/plugin/grpc-extend)
diff --git a/sa-token-doc/plugin/spel-at.md b/sa-token-doc/plugin/spel-at.md
new file mode 100644
index 00000000..05ef6475
--- /dev/null
+++ b/sa-token-doc/plugin/spel-at.md
@@ -0,0 +1,148 @@
+# SpEL 表达式注解鉴权
+
+Sa-Token 提供一个 `@SaCheckEL` 鉴权注解,该注解允许你使用 SpEL 表达式进行鉴权。
+
+
+### 1、引入插件
+
+由于该注解的工作底层需要依赖 SpringAOP 切面编程,因此你需要单独引入插件包 `sa-token-spring-el` 才可以使用此注解。
+
+
+
+``` xml
+
+
+ cn.dev33
+ sa-token-spring-el
+ ${sa.top.version}
+
+```
+
+``` gradle
+// Sa-Token 注解鉴权使用 EL 表达式
+implementation 'cn.dev33:sa-token-spring-el:${sa.top.version}'
+```
+
+
+
+### 2、简单示例
+
+以下是一些使用示例:
+``` java
+@RestController
+@RequestMapping("/check-el/")
+public class SaCheckELController {
+
+ // 登录校验
+ @SaCheckEL("stp.checkLogin()")
+ @RequestMapping("test1")
+ public SaResult test1() {
+ return SaResult.ok();
+ }
+
+ // 权限校验
+ @SaCheckEL("stp.checkPermission('user:edit')")
+ @RequestMapping("test3")
+ public SaResult test3() {
+ return SaResult.ok();
+ }
+
+ // 参数长度校验
+ @SaCheckEL("NEED( #name.length() > 3 )")
+ @RequestMapping("test5")
+ public SaResult test5(@RequestParam(defaultValue = "") String name) {
+ return SaResult.ok().set("name", name);
+ }
+
+ // SaSession 里取值校验
+ @SaCheckEL("NEED( stp.getSession().get('name') == 'zhangsan' )")
+ @RequestMapping("test8")
+ public SaResult test8() {
+ return SaResult.ok();
+ }
+
+}
+```
+
+
+### 3、多账号体系鉴权
+
+要在 EL 表达式中使用多账号体系鉴权模式,你需要在配置类中重写 `SaCheckELRootMap 扩展函数`,增加 EL 表达式可使用的根对象:
+
+``` java
+@Configuration
+public class SaTokenConfigure {
+
+ /**
+ * 重写 Sa-Token 框架内部算法策略
+ */
+ @PostConstruct
+ public void rewriteSaStrategy() {
+ // 重写 SaCheckELRootMap 扩展函数,增加注解鉴权 EL 表达式可使用的根对象
+ SaAnnotationStrategy.instance.checkELRootMapExtendFunction = rootMap -> {
+ System.out.println("--------- 执行 SaCheckELRootMap 增强,目前已包含的的跟对象包括:" + rootMap.keySet());
+ // 新增 stpUser 根对象,使之可以在表达式中通过 stpUser.checkLogin() 方式进行多账号体系鉴权
+ rootMap.put("stpUser", StpUserUtil.getStpLogic());
+ };
+ }
+
+}
+```
+
+然后就可以使用多账号体系鉴权模式了
+
+``` java
+// 多账号体系鉴权测试
+@SaCheckEL("stpUser.checkLogin()")
+@RequestMapping("test9")
+public SaResult test9() {
+ return SaResult.ok();
+}
+```
+
+
+### 4、调用本类成员变量
+``` java
+// 本模块需要鉴权的权限码
+public String permissionCode = "article:add";
+
+// 调用本类的成员变量
+@SaCheckEL("stp.checkPermission( this.permissionCode )")
+@RequestMapping("test10")
+public SaResult test10() {
+ return SaResult.ok();
+}
+```
+
+
+### 5、忽略鉴权
+配合 `@SaIgnore` 注解做到忽略某接口的鉴权
+``` java
+// 忽略鉴权测试
+@SaIgnore
+@SaCheckEL("stp.checkPermission( 'abc' )")
+@RequestMapping("test11")
+public SaResult test11() {
+ return SaResult.ok();
+}
+```
+
+
+### 6、代码提示
+
+如果在书写 SpEL 表达式时需要代码提示:
+
+
+
+可以在 idea 中安装 **SpEL Assistant** 插件,该插件由 `@ly-chn` 提供,允许为自定义注解书写 SpEL 表达式时增加代码提示功能,
+开源地址:[https://github.com/ly-chn/SpEL-Assistant](https://github.com/ly-chn/SpEL-Assistant)
+
+安装方式:直接在 idea 插件商店中搜索 “**SpEL Assistant**” 即可
+
+
+
+
+
+ 本章代码示例:Sa-Token SpEL表达式注解鉴权 —— [ SaCheckELController.java ]
+
\ No newline at end of file
diff --git a/sa-token-plugin/pom.xml b/sa-token-plugin/pom.xml
index 027d6890..a3684dd3 100644
--- a/sa-token-plugin/pom.xml
+++ b/sa-token-plugin/pom.xml
@@ -33,6 +33,7 @@
sa-token-oauth2
sa-token-quick-login
sa-token-spring-aop
+ sa-token-spring-el
sa-token-temp-jwt
sa-token-jwt
sa-token-dubbo
diff --git a/sa-token-plugin/sa-token-spring-el/pom.xml b/sa-token-plugin/sa-token-spring-el/pom.xml
new file mode 100644
index 00000000..494ac51d
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/pom.xml
@@ -0,0 +1,34 @@
+
+
+ 4.0.0
+
+
+ cn.dev33
+ sa-token-plugin
+ ${revision}
+ ../pom.xml
+
+ jar
+
+ sa-token-spring-el
+ sa-token-spring-el
+ sa-token authentication by spring-el
+
+
+
+
+ cn.dev33
+ sa-token-core
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+
+
+
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/annotation/SaCheckEL.java b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/annotation/SaCheckEL.java
new file mode 100644
index 00000000..81adcf8c
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/annotation/SaCheckEL.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020-2099 sa-token.cc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.dev33.satoken.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 注解鉴权:根据 EL 表达式执行鉴权
+ *
+ * 可标注在方法、类上(效果等同于标注在此类的所有方法上)
+ *
+ * @author click33
+ * @since 1.40.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface SaCheckEL {
+
+ /**
+ * 需要执行的 EL 表达式
+ *
+ * @return /
+ */
+ String value() default "";
+
+}
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELAspect.java b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELAspect.java
new file mode 100644
index 00000000..82a686da
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELAspect.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2020-2099 sa-token.cc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.dev33.satoken.aop;
+
+import cn.dev33.satoken.annotation.SaCheckEL;
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.strategy.SaAnnotationStrategy;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.context.expression.MapAccessor;
+import org.springframework.context.expression.MethodBasedEvaluationContext;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.util.ObjectUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * Sa-Token 注解鉴权 EL 表达式 AOP 切入 (用于处理 @SaCheckEL 注解)
+ *
+ * @author click33
+ * @since 1.40.0
+ */
+@Aspect
+public class SaCheckELAspect implements BeanFactoryAware {
+
+ /**
+ * 表达式解析器 (用于解析 EL 表达式)
+ */
+ private final ExpressionParser parser = new SpelExpressionParser();
+
+ /**
+ * 参数名发现器 (用于获取方法参数名)
+ */
+ private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
+
+ /**
+ * Spring Bean 工厂 (用于解析 Spring 容器中的 Bean 对象)
+ */
+ private BeanFactory beanFactory;
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ /**
+ * 前置通知 (所有被 SaCheckEL 注解修饰的方法或类)
+ *
+ * @param joinPoint /
+ */
+ @Before("@within(cn.dev33.satoken.annotation.SaCheckEL) || @annotation(cn.dev33.satoken.annotation.SaCheckEL)")
+ public void atBefore(JoinPoint joinPoint) {
+
+ // 获取方法签名与参数列表
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ Method method = signature.getMethod();
+ Object[] args = joinPoint.getArgs();
+
+ // 如果标注了 @SaIgnore 注解,则跳过,代表不进行校验
+ if(SaAnnotationStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
+ return;
+ }
+
+ // 1、根数据对象构建
+ // 构建校验上下文根数据对象
+ SaCheckELRootMap rootMap = new SaCheckELRootMap(method, extractArgs(method, args), joinPoint.getTarget() );
+
+ // 添加 this 指针指向注解函数所在类,使之可以在表达式中通过 this.xx 访问类的属性和方法 (与Target一致,此处只是为了更加语义化)
+ rootMap.put(SaCheckELRootMap.KEY_THIS, joinPoint.getTarget());
+
+ // 添加全局默认的 StpLogic 对象,使之可以在表达式中通过 stp.checkLogin() 方式调用校验方法
+ rootMap.put(SaCheckELRootMap.KEY_STP, StpUtil.getStpLogic());
+
+ // 添加 JoinPoint 对象,使开发者在扩展时可以根据 JoinPoint 对象获取更多信息
+ rootMap.put(SaCheckELRootMap.KEY_JOIN_POINT, joinPoint);
+
+ // 执行开发者自定义的增强策略
+ SaAnnotationStrategy.instance.checkELRootMapExtendFunction.accept(rootMap);
+
+ // 2、表达式解析方案构建
+ // 创建表达式解析上下文
+ MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(rootMap, method, args, pnd);
+
+ // 添加属性访问器,使之可以解析 Map 对象的属性作为根上下文
+ context.addPropertyAccessor(new MapAccessor());
+
+ // 设置 Bean 解析器,使之可以在表达式中引用 Spring 容器管理的所有 Bean 对象
+ context.setBeanResolver(new BeanFactoryResolver(beanFactory));
+
+ // 3、开始校验
+ // 先校验 Method 所属 Class 上的注解表达式
+ SaCheckEL ofClass = (SaCheckEL) SaAnnotationStrategy.instance.getAnnotation.apply(method.getDeclaringClass(), SaCheckEL.class);
+ if (ofClass != null) {
+ parser.parseExpression(ofClass.value()).getValue(context);
+ }
+
+ // 再校验 Method 上的注解表达式
+ SaCheckEL ofMethod = (SaCheckEL) SaAnnotationStrategy.instance.getAnnotation.apply(method, SaCheckEL.class);
+ if (ofMethod != null) {
+ parser.parseExpression(ofMethod.value()).getValue(context);
+ }
+
+ }
+
+ /**
+ * 如果是可变长参数,则展开并返回,否则原样返回
+ *
+ * @param method /
+ * @param args /
+ * @return /
+ */
+ private Object[] extractArgs(Method method, Object[] args) {
+ if (!method.isVarArgs()) {
+ return args;
+ } else {
+ Object[] varArgs = ObjectUtils.toObjectArray(args[args.length - 1]);
+ Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
+ System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
+ System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
+ return combinedArgs;
+ }
+ }
+}
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELRootMap.java b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELRootMap.java
new file mode 100644
index 00000000..125eedf3
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/java/cn/dev33/satoken/aop/SaCheckELRootMap.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020-2099 sa-token.cc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 cn.dev33.satoken.aop;
+
+import cn.dev33.satoken.error.SaErrorCode;
+import cn.dev33.satoken.exception.SaTokenException;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * Sa-Token 注解鉴权 EL 表达式解析器的根数据对象
+ *
+ * @author click33
+ * @since 1.40.0
+ */
+public class SaCheckELRootMap extends HashMap {
+
+ /**
+ * KEY标记:被切入的函数
+ */
+ public static final String KEY_METHOD = "method";
+
+ /**
+ * KEY标记:被切入的函数参数
+ */
+ public static final String KEY_ARGS = "args";
+
+ /**
+ * KEY标记:被切入的目标对象
+ */
+ public static final String KEY_TARGET = "target";
+
+ /**
+ * KEY标记:注解所在类对象引用
+ */
+ public static final String KEY_THIS = "this";
+
+ /**
+ * KEY标记:全局默认 StpLogic 对象
+ */
+ public static final String KEY_STP = "stp";
+
+ /**
+ * KEY标记:本次切入的 JoinPoint 对象
+ */
+ public static final String KEY_JOIN_POINT = "joinPoint";
+
+ public SaCheckELRootMap(Method method, Object[] args, Object target) {
+ this.put(KEY_METHOD, method);
+ this.put(KEY_ARGS, args);
+ this.put(KEY_TARGET, target);
+ }
+
+ /**
+ * 获取 被切入的函数
+ *
+ * @return method 被切入的函数
+ */
+ public Method getMethod() {
+ return (Method) this.get(KEY_METHOD);
+ }
+
+ /**
+ * 获取 被切入的函数参数
+ *
+ * @return args 被切入的函数参数
+ */
+ public Object[] getArgs() {
+ return (Object[]) this.get(KEY_ARGS);
+ }
+
+ /**
+ * 获取 被切入的目标对象
+ *
+ * @return target 被切入的目标对象
+ */
+ public Object getTarget() {
+ return this.get(KEY_TARGET);
+ }
+
+ /**
+ * 获取 注解所在类对象引用
+ *
+ * @return this 注解所在类对象引用
+ */
+ public Object getThis() {
+ return this.get(KEY_THIS);
+ }
+
+ /**
+ * 获取本次切入的 JoinPoint 对象
+ */
+ public Object getJoinPoint() {
+ return this.get(KEY_JOIN_POINT);
+ }
+
+ /**
+ * 断言函数, 表达式执行结果为true才能通过
+ *
+ * @param flag 执行结果
+ */
+ public void NEED(boolean flag) {
+ NEED(flag, SaErrorCode.CODE_UNDEFINED, "未通过 EL 表达式校验");
+ }
+
+ /**
+ * 断言函数, 表达式执行结果为true才能通过,并在未通过时抛出 SaTokenException 异常,异常描述信息为 errorMessage
+ *
+ * @param flag 执行结果
+ */
+ public void NEED(boolean flag, String errorMessage) {
+ NEED(flag, SaErrorCode.CODE_UNDEFINED, errorMessage);
+ }
+
+ /**
+ * 断言函数, 表达式执行结果为true才能通过,并在未通过时抛出 SaTokenException 异常,异常码为 errorCode,异常描述信息为 errorMessage
+ *
+ * @param flag 执行结果
+ */
+ public void NEED(boolean flag, int errorCode, String errorMessage) {
+ if(!flag) {
+ throw new SaTokenException(errorCode, errorMessage);
+ }
+ }
+
+
+}
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring.factories b/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..c2878710
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+cn.dev33.satoken.aop.SaCheckELAspect
\ No newline at end of file
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 00000000..1da378a2
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+cn.dev33.satoken.aop.SaCheckELAspect
\ No newline at end of file
diff --git a/sa-token-plugin/sa-token-spring-el/src/main/resources/spel-extension.json b/sa-token-plugin/sa-token-spring-el/src/main/resources/spel-extension.json
new file mode 100644
index 00000000..931a7be8
--- /dev/null
+++ b/sa-token-plugin/sa-token-spring-el/src/main/resources/spel-extension.json
@@ -0,0 +1,15 @@
+{
+ "cn.dev33.satoken.annotation.SaCheckEL@value": {
+ "method": {
+ "parameters": true,
+ "parametersPrefix": [
+ "p",
+ "a"
+ ]
+ },
+ "fields": {
+ "root": "cn.dev33.satoken.aop.SaCheckELRootMap",
+ "stp": "cn.dev33.satoken.stp.StpLogic"
+ }
+ }
+}
\ No newline at end of file