SaAnnotationAbstractHandler 由抽象类改为接口

This commit is contained in:
click33
2024-08-03 19:08:51 +08:00
parent 834e1d5b34
commit cd0c20793a
24 changed files with 1253 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
/*
* 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.handler;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
/**
* 所有注解处理器的父接口
*
* @author click33
* @since 2024/8/2
*/
public interface SaAnnotationAbstractHandler<T extends Annotation> {
/**
* 获取所要处理的注解类型
* @return /
*/
Class<T> getHandlerAnnotationClass();
/**
* 所需要执行的校验方法
* @param at 注解对象
* @param element 被标注的注解的元素引用(类或方法)
*/
@SuppressWarnings("unchecked")
default void check(Annotation at, AnnotatedElement element) {
checkMethod((T) at, element);
}
/**
* 所需要执行的校验方法(转换类型后)
* @param at 注解对象
* @param element 被标注的注解的元素引用(类或方法)
*/
void checkMethod(T at, AnnotatedElement element);
}

View File

@@ -0,0 +1,51 @@
/*
* 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.handler;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckDisable;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckDisable 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckDisableHandler implements SaAnnotationAbstractHandler<SaCheckDisable> {
@Override
public Class<SaCheckDisable> getHandlerAnnotationClass() {
return SaCheckDisable.class;
}
@Override
public void checkMethod(SaCheckDisable at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.level());
}
public static void _checkMethod(String type, String[] value, int level) {
StpLogic stpLogic = SaManager.getStpLogic(type, false);
Object loginId = stpLogic.getLoginId();
for (String service : value) {
stpLogic.checkDisableLevel(loginId, service, level);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.handler;
import cn.dev33.satoken.annotation.SaCheckHttpBasic;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckHttpBasic 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckHttpBasicHandler implements SaAnnotationAbstractHandler<SaCheckHttpBasic> {
@Override
public Class<SaCheckHttpBasic> getHandlerAnnotationClass() {
return SaCheckHttpBasic.class;
}
@Override
public void checkMethod(SaCheckHttpBasic at, AnnotatedElement element) {
_checkMethod(at.realm(), at.account());
}
public static void _checkMethod(String realm, String account) {
SaHttpBasicUtil.check(realm, account);
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.handler;
import cn.dev33.satoken.annotation.SaCheckHttpDigest;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.httpauth.digest.SaHttpDigestUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckHttpDigest 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckHttpDigestHandler implements SaAnnotationAbstractHandler<SaCheckHttpDigest> {
@Override
public Class<SaCheckHttpDigest> getHandlerAnnotationClass() {
return SaCheckHttpDigest.class;
}
@Override
public void checkMethod(SaCheckHttpDigest at, AnnotatedElement element) {
_checkMethod(at.username(), at.password(), at.realm(), at.value());
}
public static void _checkMethod(String username, String password, String realm, String value) {
// 如果配置了 value则以 value 优先
if(SaFoxUtil.isNotEmpty(value)){
String[] arr = value.split(":");
if(arr.length != 2){
throw new SaTokenException("注解参数配置错误格式应如username:password");
}
SaHttpDigestUtil.check(arr[0], arr[1]);
return;
}
// 如果配置了 username则分别获取参数
if(SaFoxUtil.isNotEmpty(username)){
SaHttpDigestUtil.check(username, password, realm);
return;
}
// 都没有配置,则根据全局配置参数进行校验
SaHttpDigestUtil.check();
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.handler;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckLogin 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckLoginHandler implements SaAnnotationAbstractHandler<SaCheckLogin> {
@Override
public Class<SaCheckLogin> getHandlerAnnotationClass() {
return SaCheckLogin.class;
}
@Override
public void checkMethod(SaCheckLogin at, AnnotatedElement element) {
_checkMethod(at.type());
}
public static void _checkMethod(String type) {
StpLogic stpLogic = SaManager.getStpLogic(type, false);
stpLogic.checkLogin();
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.handler;
import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 注解 SaCheckOr 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckOrHandler implements SaAnnotationAbstractHandler<SaCheckOr> {
@Override
public Class<SaCheckOr> getHandlerAnnotationClass() {
return SaCheckOr.class;
}
@Override
public void checkMethod(SaCheckOr at, AnnotatedElement element) {
_checkMethod(at.login(), at.role(), at.permission(), at.safe(), at.httpBasic(), at.httpDigest(), at.disable(), element);
}
public static void _checkMethod(
SaCheckLogin[] login,
SaCheckRole[] role,
SaCheckPermission[] permission,
SaCheckSafe[] safe,
SaCheckHttpBasic[] httpBasic,
SaCheckHttpDigest[] httpDigest,
SaCheckDisable[] disable,
AnnotatedElement element
) {
// 先把所有注解塞到一个 list 里
List<Annotation> annotationList = new ArrayList<>();
annotationList.addAll(Arrays.asList(login));
annotationList.addAll(Arrays.asList(role));
annotationList.addAll(Arrays.asList(permission));
annotationList.addAll(Arrays.asList(safe));
annotationList.addAll(Arrays.asList(disable));
annotationList.addAll(Arrays.asList(httpBasic));
annotationList.addAll(Arrays.asList(httpDigest));
// 如果 atList 为空,说明 SaCheckOr 上不包含任何注解校验,我们直接跳过即可
if(annotationList.isEmpty()) {
return;
}
// 逐个开始校验 >>>
List<SaTokenException> errorList = new ArrayList<>();
for (Annotation item : annotationList) {
try {
SaAnnotationStrategy.instance.annotationHandlerMap.get(item.annotationType()).check(item, element);
// 只要有一个校验通过,就可以直接返回了
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 执行至此,说明所有注解校验都通过不了,此时 errorList 里面会有多个异常,我们随便抛出一个即可
throw errorList.get(0);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.handler;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaFoxUtil;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckPermission 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckPermissionHandler implements SaAnnotationAbstractHandler<SaCheckPermission> {
@Override
public Class<SaCheckPermission> getHandlerAnnotationClass() {
return SaCheckPermission.class;
}
@Override
public void checkMethod(SaCheckPermission at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.mode(), at.orRole());
}
public static void _checkMethod(String type, String[] value, SaMode mode, String[] orRole) {
StpLogic stpLogic = SaManager.getStpLogic(type, false);
String[] permissionArray = value;
try {
if(mode == SaMode.AND) {
stpLogic.checkPermissionAnd(permissionArray);
} else {
stpLogic.checkPermissionOr(permissionArray);
}
} catch (NotPermissionException e) {
// 权限认证校验未通过,再开始角色认证校验
for (String role : orRole) {
String[] rArr = SaFoxUtil.convertStringToArray(role);
// 某一项 role 认证通过,则可以提前退出了,代表通过
if (stpLogic.hasRoleAnd(rArr)) {
return;
}
}
throw e;
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.handler;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckRole 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckRoleHandler implements SaAnnotationAbstractHandler<SaCheckRole> {
@Override
public Class<SaCheckRole> getHandlerAnnotationClass() {
return SaCheckRole.class;
}
@Override
public void checkMethod(SaCheckRole at, AnnotatedElement element) {
_checkMethod(at.type(), at.value(), at.mode());
}
public static void _checkMethod(String type, String[] value, SaMode mode) {
StpLogic stpLogic = SaManager.getStpLogic(type, false);
String[] roleArray = value;
if(mode == SaMode.AND) {
stpLogic.checkRoleAnd(roleArray);
} else {
stpLogic.checkRoleOr(roleArray);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.handler;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.stp.StpLogic;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaCheckSafe 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaCheckSafeHandler implements SaAnnotationAbstractHandler<SaCheckSafe> {
@Override
public Class<SaCheckSafe> getHandlerAnnotationClass() {
return SaCheckSafe.class;
}
@Override
public void checkMethod(SaCheckSafe at, AnnotatedElement element) {
_checkMethod(at.type(), at.value());
}
public static void _checkMethod(String type, String value) {
StpLogic stpLogic = SaManager.getStpLogic(type, false);
stpLogic.checkSafe(value);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.handler;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.router.SaRouter;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 SaIgnore 的处理器
*
* @author click33
* @since 2024/8/2
*/
public class SaIgnoreHandler implements SaAnnotationAbstractHandler<SaIgnore> {
@Override
public Class<SaIgnore> getHandlerAnnotationClass() {
return SaIgnore.class;
}
@Override
public void checkMethod(SaIgnore at, AnnotatedElement element) {
_checkMethod();
}
public static void _checkMethod() {
SaRouter.stop();
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.strategy;
import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.annotation.handler.*;
import cn.dev33.satoken.fun.strategy.SaCheckMethodAnnotationFunction;
import cn.dev33.satoken.fun.strategy.SaGetAnnotationFunction;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token 注解鉴权相关策略
*
* @author click33
* @since 1.39.0
*/
public final class SaAnnotationStrategy {
private SaAnnotationStrategy() {
registerDefaultAnnotationHandler();
}
/**
* 全局单例引用
*/
public static final SaAnnotationStrategy instance = new SaAnnotationStrategy();
// ----------------------- 所有策略
/**
* 注解处理器集合
*/
public Map<Class<?>, SaAnnotationAbstractHandler<?>> annotationHandlerMap = new LinkedHashMap<>();
/**
* 注册所有默认的注解处理器
*/
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());
annotationHandlerMap.put(SaCheckSafe.class, new SaCheckSafeHandler());
annotationHandlerMap.put(SaCheckDisable.class, new SaCheckDisableHandler());
annotationHandlerMap.put(SaCheckHttpBasic.class, new SaCheckHttpBasicHandler());
annotationHandlerMap.put(SaCheckHttpDigest.class, new SaCheckHttpDigestHandler());
annotationHandlerMap.put(SaCheckOr.class, new SaCheckOrHandler());
}
/**
* 注册一个注解处理器
*/
public void registerAnnotationHandler(SaAnnotationAbstractHandler<?> handler) {
annotationHandlerMap.put(handler.getHandlerAnnotationClass(), handler);
SaTokenEventCenter.doRegisterAnnotationHandler(handler);
}
/**
* 注册一个注解处理器,到首位
*/
public void registerAnnotationHandlerToFirst(SaAnnotationAbstractHandler<?> handler) {
Map<Class<?>, SaAnnotationAbstractHandler<?>> newMap = new LinkedHashMap<>();
newMap.put(handler.getHandlerAnnotationClass(), handler);
newMap.putAll(annotationHandlerMap);
this.annotationHandlerMap = newMap;
SaTokenEventCenter.doRegisterAnnotationHandler(handler);
}
/**
* 移除一个注解处理器
*/
public void removeAnnotationHandler(Class<?> cls) {
annotationHandlerMap.remove(cls);
}
/**
* 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)
*/
@SuppressWarnings("unchecked")
public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> {
// 遍历所有的注解处理器,检查此 method 是否具有这些指定的注解
for (Map.Entry<Class<?>, SaAnnotationAbstractHandler<?>> entry: annotationHandlerMap.entrySet()) {
// 先校验 Method 所属 Class 上的注解
Annotation classTakeAnnotation = instance.getAnnotation.apply(method.getDeclaringClass(), (Class<Annotation>)entry.getKey());
if(classTakeAnnotation != null) {
entry.getValue().check(classTakeAnnotation, method.getDeclaringClass());
}
// 再校验 Method 上的注解
Annotation methodTakeAnnotation = instance.getAnnotation.apply(method, (Class<Annotation>)entry.getKey());
if(methodTakeAnnotation != null) {
entry.getValue().check(methodTakeAnnotation, method);
}
}
};
/**
* 从元素上获取注解
*/
public SaGetAnnotationFunction getAnnotation = (element, annotationClass)->{
// 默认使用jdk的注解处理器
return element.getAnnotation(annotationClass);
};
}