feat(code): SaCheckOr 注解添加 append 字段,用于抓取未预先定义的注解类型进行批量注解鉴权

This commit is contained in:
click33 2025-05-15 01:04:54 +08:00
parent 3e13a39244
commit 3edac001ce
26 changed files with 125 additions and 73 deletions

View File

@ -15,10 +15,7 @@
*/
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;
import java.lang.annotation.*;
/**
* 批量注解鉴权只要满足其中一个注解即可通过验证
@ -88,4 +85,11 @@ public @interface SaCheckOr {
*/
SaCheckApiKey[] apikey() default {};
/**
* 需要追加抓取的注解 Class (只能填写 Sa-Token 相关注解类型)
*
* @return /
*/
Class<? extends Annotation>[] append() default {};
}

View File

@ -16,7 +16,7 @@
package cn.dev33.satoken.annotation.handler;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 所有注解处理器的父接口
@ -35,18 +35,18 @@ public interface SaAnnotationHandlerInterface<T extends Annotation> {
/**
* 所需要执行的校验方法
* @param at 注解对象
* @param method 被标注的注解的方法引用
* @param element 被标注的注解的元素(方法/)引用
*/
@SuppressWarnings("unchecked")
default void check(Annotation at, Method method) {
checkMethod((T) at, method);
default void check(Annotation at, AnnotatedElement element) {
checkMethod((T) at, element);
}
/**
* 所需要执行的校验方法转换类型后
* @param at 注解对象
* @param method 被标注的注解的方法引用
* @param element 被标注的注解的元素(方法/)引用
*/
void checkMethod(T at, Method method);
void checkMethod(T at, AnnotatedElement element);
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.apikey.SaApiKeyUtil;
import cn.dev33.satoken.context.SaHolder;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckApiKey 的处理器
@ -36,7 +36,7 @@ public class SaCheckApiKeyHandler implements SaAnnotationHandlerInterface<SaChec
}
@Override
public void checkMethod(SaCheckApiKey at, Method method) {
public void checkMethod(SaCheckApiKey at, AnnotatedElement element) {
_checkMethod(at.scope(), at.mode());
}

View File

@ -19,7 +19,7 @@ import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckDisable;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckDisable 的处理器
@ -35,7 +35,7 @@ public class SaCheckDisableHandler implements SaAnnotationHandlerInterface<SaChe
}
@Override
public void checkMethod(SaCheckDisable at, Method method) {
public void checkMethod(SaCheckDisable at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.level());
}

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.annotation.handler;
import cn.dev33.satoken.annotation.SaCheckHttpBasic;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckHttpBasic 的处理器
@ -34,7 +34,7 @@ public class SaCheckHttpBasicHandler implements SaAnnotationHandlerInterface<SaC
}
@Override
public void checkMethod(SaCheckHttpBasic at, Method method) {
public void checkMethod(SaCheckHttpBasic at, AnnotatedElement element) {
_checkMethod(at.realm(), at.account());
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.httpauth.digest.SaHttpDigestUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckHttpDigest 的处理器
@ -36,7 +36,7 @@ public class SaCheckHttpDigestHandler implements SaAnnotationHandlerInterface<Sa
}
@Override
public void checkMethod(SaCheckHttpDigest at, Method method) {
public void checkMethod(SaCheckHttpDigest at, AnnotatedElement element) {
_checkMethod(at.username(), at.password(), at.realm(), at.value());
}

View File

@ -19,7 +19,7 @@ import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckLogin 的处理器
@ -35,7 +35,7 @@ public class SaCheckLoginHandler implements SaAnnotationHandlerInterface<SaCheck
}
@Override
public void checkMethod(SaCheckLogin at, Method method) {
public void checkMethod(SaCheckLogin at, AnnotatedElement element) {
_checkMethod(at.type());
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -39,8 +39,8 @@ public class SaCheckOrHandler implements SaAnnotationHandlerInterface<SaCheckOr>
}
@Override
public void checkMethod(SaCheckOr at, Method method) {
_checkMethod(at.login(), at.role(), at.permission(), at.safe(), at.httpBasic(), at.httpDigest(), at.disable(), at.apikey(), method);
public void checkMethod(SaCheckOr at, AnnotatedElement element) {
_checkMethod(at.login(), at.role(), at.permission(), at.safe(), at.httpBasic(), at.httpDigest(), at.disable(), at.apikey(), at.append(), element);
}
public static void _checkMethod(
@ -52,7 +52,8 @@ public class SaCheckOrHandler implements SaAnnotationHandlerInterface<SaCheckOr>
SaCheckHttpDigest[] httpDigest,
SaCheckDisable[] disable,
SaCheckApiKey[] apikey,
Method method
Class<? extends Annotation>[] append,
AnnotatedElement element
) {
// 先把所有注解塞到一个 list
List<Annotation> annotationList = new ArrayList<>();
@ -64,6 +65,12 @@ public class SaCheckOrHandler implements SaAnnotationHandlerInterface<SaCheckOr>
annotationList.addAll(Arrays.asList(httpBasic));
annotationList.addAll(Arrays.asList(httpDigest));
annotationList.addAll(Arrays.asList(apikey));
for (Class<? extends Annotation> annotationClass : append) {
Annotation annotation = SaAnnotationStrategy.instance.getAnnotation.apply(element, annotationClass);
if(annotation != null) {
annotationList.add(annotation);
}
}
// 如果 atList 为空说明 SaCheckOr 上不包含任何注解校验我们直接跳过即可
if(annotationList.isEmpty()) {
@ -74,7 +81,7 @@ public class SaCheckOrHandler implements SaAnnotationHandlerInterface<SaCheckOr>
List<SaTokenException> errorList = new ArrayList<>();
for (Annotation item : annotationList) {
try {
SaAnnotationStrategy.instance.annotationHandlerMap.get(item.annotationType()).check(item, method);
SaAnnotationStrategy.instance.annotationHandlerMap.get(item.annotationType()).check(item, element);
// 只要有一个校验通过就可以直接返回了
return;
} catch (SaTokenException e) {

View File

@ -22,7 +22,7 @@ import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckPermission 的处理器
@ -38,7 +38,7 @@ public class SaCheckPermissionHandler implements SaAnnotationHandlerInterface<Sa
}
@Override
public void checkMethod(SaCheckPermission at, Method method) {
public void checkMethod(SaCheckPermission at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.mode(), at.orRole());
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckRole 的处理器
@ -36,7 +36,7 @@ public class SaCheckRoleHandler implements SaAnnotationHandlerInterface<SaCheckR
}
@Override
public void checkMethod(SaCheckRole at, Method method) {
public void checkMethod(SaCheckRole at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.mode());
}

View File

@ -19,7 +19,7 @@ import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckSafe 的处理器
@ -35,7 +35,7 @@ public class SaCheckSafeHandler implements SaAnnotationHandlerInterface<SaCheckS
}
@Override
public void checkMethod(SaCheckSafe at, Method method) {
public void checkMethod(SaCheckSafe at, AnnotatedElement element) {
_checkMethod(at.type(), at.value());
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sign.SaSignMany;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckSign 的处理器
@ -36,7 +36,7 @@ public class SaCheckSignHandler implements SaAnnotationHandlerInterface<SaCheckS
}
@Override
public void checkMethod(SaCheckSign at, Method method) {
public void checkMethod(SaCheckSign at, AnnotatedElement element) {
_checkMethod(at.appid(), at.verifyParams());
}

View File

@ -18,10 +18,11 @@ package cn.dev33.satoken.annotation.handler;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.router.SaRouter;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaIgnore 的处理器
* <h2> v1.43.0 版本起SaIgnore 注解处理逻辑已转移到全局策略中此处理器代码仅做留档 </h2>
*
* @author click33
* @since 2024/8/2
@ -34,7 +35,7 @@ public class SaIgnoreHandler implements SaAnnotationHandlerInterface<SaIgnore> {
}
@Override
public void checkMethod(SaIgnore at, Method method) {
public void checkMethod(SaIgnore at, AnnotatedElement element) {
_checkMethod();
}

View File

@ -155,8 +155,8 @@ public class SaTotpTemplate {
* 生成谷歌认证器的扫码字符串 (形如otpauth://totp/{issuer}:{account}?secret={secretKey}&issuer={issuer})
*
* @param account 账户名
* @param secretKey TOTP 秘钥
* @param issuer 签发者
* @param secretKey TOTP 秘钥
* @return /
*/
public String generateGoogleSecretKey(String account, String issuer, String secretKey) {

View File

@ -88,4 +88,16 @@ public class SaTotpUtil {
return SaManager.getSaTotpTemplate().generateGoogleSecretKey(account, secretKey);
}
/**
* 生成谷歌认证器的扫码字符串 (形如otpauth://totp/{issuer}:{account}?secret={secretKey}&issuer={issuer})
*
* @param account 账户名
* @param issuer 签发者
* @param secretKey TOTP 秘钥
* @return /
*/
public static String generateGoogleSecretKey(String account, String issuer, String secretKey) {
return SaManager.getSaTotpTemplate().generateGoogleSecretKey(account, issuer, secretKey);
}
}

View File

@ -17,15 +17,12 @@ package cn.dev33.satoken.strategy;
import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.annotation.handler.*;
import cn.dev33.satoken.fun.strategy.SaCheckELRootMapExtendFunction;
import cn.dev33.satoken.fun.strategy.SaCheckMethodAnnotationFunction;
import cn.dev33.satoken.fun.strategy.SaGetAnnotationFunction;
import cn.dev33.satoken.fun.strategy.SaIsAnnotationPresentFunction;
import cn.dev33.satoken.fun.strategy.*;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import cn.dev33.satoken.router.SaRouter;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
/**
* Sa-Token 注解鉴权相关策略
@ -56,7 +53,6 @@ public final class SaAnnotationStrategy {
* 注册所有默认的注解处理器
*/
public void registerDefaultAnnotationHandler() {
annotationHandlerMap.put(SaIgnore.class, new SaIgnoreHandler());
annotationHandlerMap.put(SaCheckLogin.class, new SaCheckLoginHandler());
annotationHandlerMap.put(SaCheckRole.class, new SaCheckRoleHandler());
annotationHandlerMap.put(SaCheckPermission.class, new SaCheckPermissionHandler());
@ -98,21 +94,42 @@ public final class SaAnnotationStrategy {
/**
* 对一个 [Method] 对象进行注解校验 注解鉴权内部实现
*/
@SuppressWarnings("unchecked")
public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> {
// 遍历所有的注解处理器检查此 method 是否具有这些指定的注解
// 如果 Method 或其所属 Class 上有 @SaIgnore 注解则直接跳过整个校验过程
if(instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
SaRouter.stop();
}
// 先校验 Method 所属 Class 上的注解
instance.checkElementAnnotation.accept(method.getDeclaringClass());
// 再校验 Method 上的注解
instance.checkElementAnnotation.accept(method);
};
/**
* 对一个 [Element] 对象进行注解校验 注解鉴权内部实现
*/
@SuppressWarnings("unchecked")
public SaCheckElementAnnotationFunction checkElementAnnotation = (element) -> {
// 如果此元素上标注了 @SaCheckOr则必须在后续判断中忽略掉其指定的 append() 类型注解判断
List<Class<? extends Annotation>> ignoreClassList = new ArrayList<>();
SaCheckOr checkOr = (SaCheckOr)instance.getAnnotation.apply(element, SaCheckOr.class);
if(checkOr != null) {
ignoreClassList = Arrays.asList(checkOr.append());
}
// 遍历所有的注解处理器检查此 element 是否具有这些指定的注解
for (Map.Entry<Class<?>, SaAnnotationHandlerInterface<?>> entry: annotationHandlerMap.entrySet()) {
// 先校验 Method 所属 Class 上的注解
Annotation classTakeAnnotation = instance.getAnnotation.apply(method.getDeclaringClass(), (Class<Annotation>)entry.getKey());
if(classTakeAnnotation != null) {
entry.getValue().check(classTakeAnnotation, method);
// 忽略掉在 @SaCheckOr append 字段指定的注解
Class<Annotation> atClass = (Class<Annotation>)entry.getKey();
if(ignoreClassList.contains(atClass)) {
continue;
}
// 再校验 Method 上的注解
Annotation methodTakeAnnotation = instance.getAnnotation.apply(method, (Class<Annotation>)entry.getKey());
if(methodTakeAnnotation != null) {
entry.getValue().check(methodTakeAnnotation, method);
Annotation annotation = instance.getAnnotation.apply(element, atClass);
if(annotation != null) {
entry.getValue().check(annotation, element);
}
}
};
@ -121,7 +138,6 @@ public final class SaAnnotationStrategy {
* 从元素上获取注解
*/
public SaGetAnnotationFunction getAnnotation = (element, annotationClass)->{
// 默认使用jdk的注解处理器
return element.getAnnotation(annotationClass);
};

View File

@ -6,7 +6,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import com.pj.satoken.custom_annotation.CheckAccount;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 CheckAccount 的处理器
@ -25,7 +25,7 @@ public class CheckAccountHandler implements SaAnnotationHandlerInterface<CheckAc
// 每次请求校验注解时会执行的方法
@Override
public void checkMethod(CheckAccount at, Method method) {
public void checkMethod(CheckAccount at, AnnotatedElement element) {
// 获取前端请求提交的参数
String name = SaHolder.getRequest().getParamNotNull("name");
String pwd = SaHolder.getRequest().getParamNotNull("pwd");

View File

@ -6,7 +6,7 @@ import com.pj.satoken.StpUserUtil;
import com.pj.satoken.custom_annotation.SaUserCheckLogin;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaUserCheckLogin 的处理器
@ -22,7 +22,7 @@ public class SaUserCheckLoginHandler implements SaAnnotationHandlerInterface<SaU
}
@Override
public void checkMethod(SaUserCheckLogin at, Method method) {
public void checkMethod(SaUserCheckLogin at, AnnotatedElement element) {
SaCheckLoginHandler._checkMethod(StpUserUtil.TYPE);
}

View File

@ -6,7 +6,7 @@ import com.pj.satoken.StpUserUtil;
import com.pj.satoken.custom_annotation.SaUserCheckPermission;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaUserCheckPermission 的处理器
@ -22,7 +22,7 @@ public class SaUserCheckPermissionHandler implements SaAnnotationHandlerInterfac
}
@Override
public void checkMethod(SaUserCheckPermission at, Method method) {
public void checkMethod(SaUserCheckPermission at, AnnotatedElement element) {
SaCheckPermissionHandler._checkMethod(StpUserUtil.TYPE, at.value(), at.mode(), at.orRole());
}

View File

@ -6,7 +6,7 @@ import com.pj.satoken.StpUserUtil;
import com.pj.satoken.custom_annotation.SaUserCheckRole;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaUserCheckRole 的处理器
@ -22,7 +22,7 @@ public class SaUserCheckRoleHandler implements SaAnnotationHandlerInterface<SaUs
}
@Override
public void checkMethod(SaUserCheckRole at, Method method) {
public void checkMethod(SaUserCheckRole at, AnnotatedElement element) {
SaCheckRoleHandler._checkMethod(StpUserUtil.TYPE, at.value(), at.mode());
}

View File

@ -6,7 +6,7 @@ import com.pj.satoken.StpUserUtil;
import com.pj.satoken.custom_annotation.SaUserCheckSafe;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaUserCheckPermission 的处理器
@ -22,7 +22,7 @@ public class SaUserCheckSafeHandler implements SaAnnotationHandlerInterface<SaUs
}
@Override
public void checkMethod(SaUserCheckSafe at, Method method) {
public void checkMethod(SaUserCheckSafe at, AnnotatedElement element) {
SaCheckSafeHandler._checkMethod(StpUserUtil.TYPE, at.value());
}

View File

@ -69,7 +69,7 @@ public class AtController {
public SaResult checkSafe() {
return SaResult.ok();
}
// 通过Basic认证后才可以进入 ---- http://localhost:8081/at/checkBasic
@SaCheckHttpBasic(account = "sa:123456")
@RequestMapping("checkBasic")

View File

@ -213,6 +213,18 @@ public SaResult test() {
```
使用 append 字段追加抓取扩展包里的注解,例如:
``` java
// 测试:只有通过登录校验,或者提供了正确的 ApiKey才可以进入方法
@RequestMapping("/test")
@SaCheckOr(login = @SaCheckLogin, append = { SaCheckApiKey.class })
@SaCheckApiKey
public SaResult test() {
// ...
return SaResult.ok();
}
```
### 7、扩展阅读

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.annotation.SaCheckAccessToken;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckAccessToken 的处理器
@ -36,7 +36,7 @@ public class SaCheckAccessTokenHandler implements SaAnnotationHandlerInterface<S
}
@Override
public void checkMethod(SaCheckAccessToken at, Method method) {
public void checkMethod(SaCheckAccessToken at, AnnotatedElement element) {
_checkMethod(at.scope());
}

View File

@ -19,7 +19,7 @@ import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface;
import cn.dev33.satoken.oauth2.annotation.SaCheckClientIdSecret;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckClientSecret 的处理器
@ -35,7 +35,7 @@ public class SaCheckClientIdSecretHandler implements SaAnnotationHandlerInterfac
}
@Override
public void checkMethod(SaCheckClientIdSecret at, Method method) {
public void checkMethod(SaCheckClientIdSecret at, AnnotatedElement element) {
_checkMethod();
}

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.annotation.SaCheckClientToken;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckAccessToken 的处理器
@ -36,7 +36,7 @@ public class SaCheckClientTokenHandler implements SaAnnotationHandlerInterface<S
}
@Override
public void checkMethod(SaCheckClientToken at, Method method) {
public void checkMethod(SaCheckClientToken at, AnnotatedElement element) {
_checkMethod(at.scope());
}