新增 SaCheckOr 注解,批量注解鉴权:只要满足其中一个注解即可通过验证

This commit is contained in:
click33 2023-05-23 05:01:16 +08:00
parent 4496242626
commit 4cf317818b
25 changed files with 241 additions and 45 deletions

View File

@ -323,7 +323,7 @@ public class SaManager {
synchronized (SaManager.class) {
stpLogic = stpLogicMap.get(loginType);
if(stpLogic == null) {
stpLogic = SaStrategy.me.createStpLogic.apply(loginType);
stpLogic = SaStrategy.instance.createStpLogic.apply(loginType);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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;
/**
* 批量注解鉴权只要满足其中一个注解即可通过验证
*
* <p> 可标注在方法类上效果等同于标注在此类的所有方法上
*
* @author kong
* @since 1.35.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface SaCheckOr {
/**
* 设定 @SaCheckLogin参考 {@link SaCheckLogin}
*
* @return /
*/
SaCheckLogin[] login() default {};
/**
* 设定 @SaCheckPermission参考 {@link SaCheckPermission}
*
* @return /
*/
SaCheckPermission[] permission() default {};
/**
* 设定 @SaCheckRole参考 {@link SaCheckRole}
*
* @return /
*/
SaCheckRole[] role() default {};
/**
* 设定 @SaCheckSafe参考 {@link SaCheckSafe}
*
* @return /
*/
SaCheckSafe[] safe() default {};
/**
* 设定 @SaCheckBasic参考 {@link SaCheckBasic}
*
* @return /
*/
SaCheckBasic[] basic() default {};
/**
* 设定 @SaCheckDisable参考 {@link SaCheckDisable}
*
* @return /
*/
SaCheckDisable[] disable() default {};
}

View File

@ -77,7 +77,7 @@ public class SaSessionCustomUtil {
public static SaSession getSessionById(String sessionId, boolean isCreate) {
SaSession session = SaManager.getSaTokenDao().getSession(splicingSessionKey(sessionId));
if (session == null && isCreate) {
session = SaStrategy.me.createSession.apply(splicingSessionKey(sessionId));
session = SaStrategy.instance.createSession.apply(splicingSessionKey(sessionId));
session.setType(SaTokenConsts.SESSION_TYPE__CUSTOM);
SaManager.getSaTokenDao().setSession(session, SaManager.getConfig().getTimeout());
}

View File

@ -123,7 +123,7 @@ public class StpLogic {
* @return 生成的tokenValue
*/
public String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
return SaStrategy.me.createToken.apply(loginId, loginType);
return SaStrategy.instance.createToken.apply(loginId, loginType);
}
/**
@ -501,7 +501,7 @@ public class StpLogic {
}
// 4如果代码走到此处说明未能成功复用旧 token需要根据算法新建 token
return SaStrategy.me.generateUniqueToken.execute(
return SaStrategy.instance.generateUniqueToken.execute(
"token",
getConfigOfMaxTryTimes(),
() -> {
@ -1096,7 +1096,7 @@ public class StpLogic {
if(session == null && isCreate) {
// 创建这个 SaSession
session = SaStrategy.me.createSession.apply(sessionId);
session = SaStrategy.instance.createSession.apply(sessionId);
// 追加操作
if(appendOperation != null) {
@ -1268,7 +1268,7 @@ public class StpLogic {
*/
if(isCreate) {
// 随机创建一个 Token
tokenValue = SaStrategy.me.generateUniqueToken.execute(
tokenValue = SaStrategy.instance.generateUniqueToken.execute(
"token",
getConfigOfMaxTryTimes(),
() -> {
@ -2099,8 +2099,8 @@ public class StpLogic {
this.checkDisableLevel(loginId, service, at.level());
}
}
// ------------------- 账号封禁 -------------------
/**
@ -2715,7 +2715,7 @@ public class StpLogic {
* @return /
*/
public boolean hasElement(List<String> list, String element) {
return SaStrategy.me.hasElement.apply(list, element);
return SaStrategy.instance.hasElement.apply(list, element);
}
/**

View File

@ -28,6 +28,7 @@ import cn.dev33.satoken.util.SaTokenConsts;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.BiFunction;
@ -58,7 +59,8 @@ public final class SaStrategy {
/**
* 获取 SaStrategy 对象的单例引用
*/
public static final SaStrategy me = new SaStrategy();
public static final SaStrategy instance = new SaStrategy();
// ----------------------- 所有策略
@ -145,10 +147,10 @@ public final class SaStrategy {
public Consumer<Method> checkMethodAnnotation = (method) -> {
// 先校验 Method 所属 Class 上的注解
me.checkElementAnnotation.accept(method.getDeclaringClass());
instance.checkElementAnnotation.accept(method.getDeclaringClass());
// 再校验 Method 上的注解
me.checkElementAnnotation.accept(method);
instance.checkElementAnnotation.accept(method);
};
/**
@ -156,41 +158,135 @@ public final class SaStrategy {
* <p> 参数 [element元素]
*/
public Consumer<AnnotatedElement> checkElementAnnotation = (target) -> {
// 校验 @SaCheckLogin 注解
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.instance.getAnnotation.apply(target, SaCheckLogin.class);
if(checkLogin != null) {
SaManager.getStpLogic(checkLogin.type(), false).checkByAnnotation(checkLogin);
}
// 校验 @SaCheckRole 注解
SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
SaCheckRole checkRole = (SaCheckRole) SaStrategy.instance.getAnnotation.apply(target, SaCheckRole.class);
if(checkRole != null) {
SaManager.getStpLogic(checkRole.type(), false).checkByAnnotation(checkRole);
}
// 校验 @SaCheckPermission 注解
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.instance.getAnnotation.apply(target, SaCheckPermission.class);
if(checkPermission != null) {
SaManager.getStpLogic(checkPermission.type(), false).checkByAnnotation(checkPermission);
}
// 校验 @SaCheckSafe 注解
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.instance.getAnnotation.apply(target, SaCheckSafe.class);
if(checkSafe != null) {
SaManager.getStpLogic(checkSafe.type(), false).checkByAnnotation(checkSafe);
}
// 校验 @SaCheckDisable 注解
SaCheckDisable checkDisable = (SaCheckDisable) SaStrategy.me.getAnnotation.apply(target, SaCheckDisable.class);
SaCheckDisable checkDisable = (SaCheckDisable) SaStrategy.instance.getAnnotation.apply(target, SaCheckDisable.class);
if(checkDisable != null) {
SaManager.getStpLogic(checkDisable.type(), false).checkByAnnotation(checkDisable);
}
// 校验 @SaCheckBasic 注解
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.instance.getAnnotation.apply(target, SaCheckBasic.class);
if(checkBasic != null) {
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
}
// 校验 @SaCheckOr 注解
SaCheckOr checkOr = (SaCheckOr) SaStrategy.instance.getAnnotation.apply(target, SaCheckOr.class);
if(checkOr != null) {
SaStrategy.instance.checkOrAnnotation.accept(checkOr);
}
};
/**
* 对一个 @SaCheckOr 进行注解校验
* <p> 参数 [SaCheckOr 注解的实例]
*/
public Consumer<SaCheckOr> checkOrAnnotation = (at) -> {
// 记录校验过程中所有的异常
List<SaTokenException> errorList = new ArrayList<>();
// 逐个开始校验 >>>
// 1校验注解@SaCheckLogin
SaCheckLogin[] checkLoginArray = at.login();
for (SaCheckLogin item : checkLoginArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 2校验注解@SaCheckRole
SaCheckRole[] checkRoleArray = at.role();
for (SaCheckRole item : checkRoleArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 3校验注解@SaCheckPermission
SaCheckPermission[] checkPermissionArray = at.permission();
for (SaCheckPermission item : checkPermissionArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 4校验注解@SaCheckSafe
SaCheckSafe[] checkSafeArray = at.safe();
for (SaCheckSafe item : checkSafeArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 5校验注解@SaCheckDisable
SaCheckDisable[] checkDisableArray = at.disable();
for (SaCheckDisable item : checkDisableArray) {
try {
SaManager.getStpLogic(item.type(), false).checkByAnnotation(item);
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 6校验注解@SaCheckBasic
SaCheckBasic[] checkBasicArray = at.basic();
for (SaCheckBasic item : checkBasicArray) {
try {
SaBasicUtil.check(item.realm(), item.account());
return;
} catch (SaTokenException e) {
errorList.add(e);
}
}
// 如果执行到这里有两种可能
// 可能 1. SaCheckOr 注解上不包含任何注解校验此时 errorList 里面一个异常都没有我们直接跳过即可
// 可能 2. 所有注解校验都通过不了此时 errorList 里面会有多个异常我们随便抛出一个即可
if(errorList.size() == 0) {
// return;
} else {
throw errorList.get(0);
}
};
/**
@ -207,8 +303,8 @@ public final class SaStrategy {
* <p> 参数 [Method, 注解]
*/
public BiFunction<Method, Class<? extends Annotation>, Boolean> isAnnotationPresent = (method, annotationClass) -> {
return me.getAnnotation.apply(method, annotationClass) != null ||
me.getAnnotation.apply(method.getDeclaringClass(), annotationClass) != null;
return instance.getAnnotation.apply(method, annotationClass) != null ||
instance.getAnnotation.apply(method.getDeclaringClass(), annotationClass) != null;
};
/**
@ -308,6 +404,18 @@ public final class SaStrategy {
return this;
}
/**
* 对一个 @SaCheckOr 进行注解校验
* <p> 参数 [SaCheckOr 注解的实例]
*
* @param checkOrAnnotation /
* @return 对象自身
*/
public SaStrategy setCheckOrAnnotation(Consumer<SaCheckOr> checkOrAnnotation) {
this.checkOrAnnotation = checkOrAnnotation;
return this;
}
/**
* 从元素上获取注解
* <p> 参数 [element元素要获取的注解类型]
@ -355,4 +463,12 @@ public final class SaStrategy {
return this;
}
//
/**
* 请更换为 instance
*/
@Deprecated
public static final SaStrategy me = instance;
}

View File

@ -54,7 +54,7 @@ public interface SaTempInterface {
default String createToken(String service, Object value, long timeout) {
// 生成 token
String token = SaStrategy.me.createToken.apply(null, null);
String token = SaStrategy.instance.createToken.apply(null, null);
// 持久化映射关系
String key = splicingKeyTempToken(service, token);

View File

@ -117,7 +117,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
@Autowired
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
SaStrategy.instance.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
}

View File

@ -7,6 +7,7 @@ import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.stereotype.Component;
import java.util.List;
@ -16,6 +17,7 @@ import java.util.List;
* @author click33
* @since <= 1.34.0
*/
@Component
public class StpUserUtil {
private StpUserUtil() {}

View File

@ -7,6 +7,7 @@ import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.stereotype.Component;
import java.util.List;
@ -16,6 +17,7 @@ import java.util.List;
* @author click33
* @since <= 1.34.0
*/
@Component
public class StpUserUtil {
private StpUserUtil() {}

View File

@ -1,7 +1,7 @@
package com.pj.test;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.pj.satoken.StpUserUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@ -18,7 +18,7 @@ public class TestController {
// 测试登录 ---- http://localhost:8081/test/login
@RequestMapping("login")
public SaResult login(@RequestParam(defaultValue = "10001") long id) {
StpUtil.login(id);
StpUserUtil.login(id);
return SaResult.ok("登录成功");
}
@ -26,7 +26,6 @@ public class TestController {
@RequestMapping("test")
public SaResult test() {
System.out.println("------------进来了");
StpUtil.getExtra("name");
// 返回
return SaResult.data("");
}

View File

@ -398,7 +398,7 @@ public class SaOAuth2Template {
// 3是否在[允许地址列表]之中
List<String> allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl);
if( ! SaStrategy.me.hasElement.apply(allowList, url)) {
if( ! SaStrategy.instance.hasElement.apply(allowList, url)) {
throw new SaOAuth2Exception("非法redirect_url" + url).setCode(SaOAuth2ErrorCode.CODE_30114);
}
}

View File

@ -64,7 +64,7 @@ public class SaTokenDaoRedisFastjson implements SaTokenDao {
}
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForFastjsonCustomized(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForFastjsonCustomized(sessionId);
// 指定相应的序列化方案
StringRedisSerializer keySerializer = new StringRedisSerializer();

View File

@ -64,7 +64,7 @@ public class SaTokenDaoRedisFastjson2 implements SaTokenDao {
}
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForFastjson2Customized(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForFastjson2Customized(sessionId);
// 指定相应的序列化方案
StringRedisSerializer keySerializer = new StringRedisSerializer();

View File

@ -127,7 +127,7 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
this.objectMapper.registerModule(timeModule);
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
} catch (Exception e) {
System.err.println(e.getMessage());
}

View File

@ -120,7 +120,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao {
timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));
this.objectMapper.registerModule(timeModule);
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
} catch (Exception e) {
System.err.println(e.getMessage());
}

View File

@ -44,7 +44,7 @@ public class SaTokenDaoOfRedisJson implements SaTokenDao {
redisBucket = redisClient.getBucket();
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForJson(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJson(sessionId);
}

View File

@ -83,11 +83,11 @@ public class SaCheckAspect {
Method method = signature.getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if(SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
if(SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
// ...
} else {
// 注解鉴权
SaStrategy.me.checkMethodAnnotation.accept(method);
SaStrategy.instance.checkMethodAnnotation.accept(method);
}
// 执行原有逻辑

View File

@ -283,7 +283,7 @@ public class SaSsoTemplate {
// 3是否在[允许地址列表]之中
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
if( ! SaStrategy.me.hasElement.apply(authUrlList, url) ) {
if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) {
throw new SaSsoException("非法redirect" + url).setCode(SaSsoErrorCode.CODE_30002);
}

View File

@ -25,7 +25,7 @@ import com.jfinal.aop.Invocation;
public class SaAnnotationInterceptor implements Interceptor {
@Override
public void intercept(Invocation invocation) {
SaStrategy.me.checkMethodAnnotation.accept((invocation.getMethod()));
SaStrategy.instance.checkMethodAnnotation.accept((invocation.getMethod()));
invocation.invoke();
}
}

View File

@ -25,7 +25,7 @@ import com.jfinal.aop.Invocation;
public class SaAnnotationInterceptor implements Interceptor {
@Override
public void intercept(Invocation invocation) {
SaStrategy.me.checkMethodAnnotation.accept((invocation.getMethod()));
SaStrategy.instance.checkMethodAnnotation.accept((invocation.getMethod()));
invocation.invoke();
}
}

View File

@ -45,7 +45,7 @@ public class SaTokenDaoOfRedisJson implements SaTokenDao {
redisBucket = redisClient.getBucket();
// 重写 SaSession 生成策略
SaStrategy.me.createSession = (sessionId) -> new SaSessionForJson(sessionId);
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJson(sessionId);
}

View File

@ -181,12 +181,12 @@ public class SaTokenFilter implements SaFilter, Filter { //之所以改名,为
Method method = action.method().getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if (SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
if (SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
return false;
}
// 注解校验
SaStrategy.me.checkMethodAnnotation.accept(method);
SaStrategy.instance.checkMethodAnnotation.accept(method);
}
return true;

View File

@ -230,12 +230,12 @@ public class SaTokenInterceptor implements RouterInterceptor {
Method method = action.method().getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if (SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
if (SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
return false;
}
// 注解校验
SaStrategy.me.checkMethodAnnotation.accept(method);
SaStrategy.instance.checkMethodAnnotation.accept(method);
}
return true;

View File

@ -100,13 +100,13 @@ public class SaInterceptor implements HandlerInterceptor {
Method method = ((HandlerMethod) handler).getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if(SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
if(SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
// 注意这里直接就退出整个鉴权了最底部的 auth.run() 路由拦截鉴权也被跳出了
return true;
}
// 执行注解鉴权
SaStrategy.me.checkMethodAnnotation.accept(method);
SaStrategy.instance.checkMethodAnnotation.accept(method);
}
// Auth 路由拦截鉴权校验

View File

@ -101,13 +101,13 @@ public class SaInterceptor implements HandlerInterceptor {
Method method = ((HandlerMethod) handler).getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore则忽略掉鉴权
if(SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
if(SaStrategy.instance.isAnnotationPresent.apply(method, SaIgnore.class)) {
// 注意这里直接就退出整个鉴权了最底部的 auth.run() 路由拦截鉴权也被跳出了
return true;
}
// 注解校验
SaStrategy.me.checkMethodAnnotation.accept(method);
SaStrategy.instance.checkMethodAnnotation.accept(method);
}
// Auth 校验