diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/model/wrapperInfo/SaDisableWrapperInfo.java b/sa-token-core/src/main/java/cn/dev33/satoken/model/wrapperInfo/SaDisableWrapperInfo.java
new file mode 100644
index 00000000..03cca096
--- /dev/null
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/model/wrapperInfo/SaDisableWrapperInfo.java
@@ -0,0 +1,119 @@
+/*
+ * 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.model.wrapperInfo;
+
+/**
+ * 返回值包装类:描述一个账号是否已被封禁等信息
+ *
+ * @author click33
+ * @since 1.40.0
+ */
+public class SaDisableWrapperInfo {
+
+ /**
+ * 是否被封禁
+ */
+ public boolean isDisable;
+
+ /**
+ * 封禁剩余时间,单位:秒(-1=永久封禁,0 or -2=未封禁)
+ */
+ public long disableTime;
+
+ /**
+ * 封禁等级(最小1级,0=未封禁)
+ */
+ public int disableLevel;
+
+ /**
+ * 构建对象
+ *
+ * @param isDisable 是否被封禁
+ * @param disableTime 封禁剩余时间,单位:秒(-1=永久封禁,0 or -2=未封禁)
+ * @param disableLevel 封禁等级(最小1级,0=未封禁)
+ */
+ public SaDisableWrapperInfo(boolean isDisable, long disableTime, int disableLevel) {
+ this.isDisable = isDisable;
+ this.disableTime = disableTime;
+ this.disableLevel = disableLevel;
+ }
+
+ /**
+ * 创建一个已封禁描述对象
+ * @param disableTime 封禁时间
+ * @param disableLevel 封禁等级
+ * @return /
+ */
+ public static SaDisableWrapperInfo createDisabled(long disableTime, int disableLevel) {
+ return new SaDisableWrapperInfo(true, disableTime, disableLevel);
+ }
+
+ /**
+ * 创建一个未封禁描述对象
+ * @return /
+ */
+ public static SaDisableWrapperInfo createNotDisabled() {
+ return new SaDisableWrapperInfo(false, 0, 0);
+ }
+
+ /**
+ * 创建一个未封禁描述对象,并指定缓存时间,指定时间内不再重复查询
+ * @param cacheTime 缓存时间(单位:秒)
+ * @return /
+ */
+ public static SaDisableWrapperInfo createNotDisabled(long cacheTime) {
+ return new SaDisableWrapperInfo(false, cacheTime, 0);
+ }
+
+ @Override
+ public String toString() {
+ return "SaDisableWrapperInfo{" +
+ "isDisable=" + isDisable +
+ ", disableTime=" + disableTime +
+ ", disableLevel=" + disableLevel +
+ '}';
+ }
+
+ // setter / getter 仅为兼容部分框架序列化操作,不建议调用
+
+ public boolean getIsDisable() {
+ return isDisable;
+ }
+
+ public SaDisableWrapperInfo setIsDisable(boolean isDisable) {
+ this.isDisable = isDisable;
+ return this;
+ }
+
+ public long getDisableTime() {
+ return disableTime;
+ }
+
+ public SaDisableWrapperInfo setDisableTime(long disableTime) {
+ this.disableTime = disableTime;
+ return this;
+ }
+
+ public int getDisableLevel() {
+ return disableLevel;
+ }
+
+ public SaDisableWrapperInfo setDisableLevel(int disableLevel) {
+ this.disableLevel = disableLevel;
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpInterface.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpInterface.java
index 651f5f15..b062e734 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpInterface.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpInterface.java
@@ -15,10 +15,12 @@
*/
package cn.dev33.satoken.stp;
+import cn.dev33.satoken.model.wrapperInfo.SaDisableWrapperInfo;
+
import java.util.List;
/**
- * 权限数据加载源接口
+ * 权限数据源加载接口
*
*
* 在使用权限校验 API 之前,你必须实现此接口,告诉框架哪些用户拥有哪些权限。
@@ -48,4 +50,15 @@ public interface StpInterface {
*/
List getRoleList(Object loginId, String loginType);
+ /**
+ * 返回指定账号 id 是否被封禁
+ *
+ * @param loginId 账号id
+ * @param service 业务标识符
+ * @return 描述该账号是否封禁的包装信息对象
+ */
+ default SaDisableWrapperInfo isDisabled(Object loginId, String service) {
+ return SaDisableWrapperInfo.createNotDisabled();
+ }
+
}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
index ba8c1bd5..5bcdc27f 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
@@ -29,6 +29,7 @@ import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.*;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.listener.SaTokenEventCenter;
+import cn.dev33.satoken.model.wrapperInfo.SaDisableWrapperInfo;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.strategy.SaStrategy;
@@ -2413,8 +2414,8 @@ public class StpLogic {
if(SaFoxUtil.isEmpty(service)) {
throw new SaTokenException("请提供要封禁的服务").setCode(SaErrorCode.CODE_11063);
}
- if(level < SaTokenConsts.MIN_DISABLE_LEVEL) {
- throw new SaTokenException("封禁等级不可以小于最小值:" + SaTokenConsts.MIN_DISABLE_LEVEL).setCode(SaErrorCode.CODE_11064);
+ if(level < SaTokenConsts.MIN_DISABLE_LEVEL && level != 0) {
+ throw new SaTokenException("封禁等级不可以小于最小值:" + SaTokenConsts.MIN_DISABLE_LEVEL + " (0除外)").setCode(SaErrorCode.CODE_11064);
}
// 打上封禁标记
@@ -2473,13 +2474,12 @@ public class StpLogic {
*/
public void checkDisableLevel(Object loginId, String service, int level) {
// 1、先前置检查一下这个账号是否被封禁了
- String value = getSaTokenDao().get(splicingKeyDisable(loginId, service));
- if(SaFoxUtil.isEmpty(value)) {
+ int disableLevel = getDisableLevel(loginId, service);
+ if(disableLevel == SaTokenConsts.NOT_DISABLE_LEVEL) {
return;
}
// 2、再判断被封禁的等级是否达到了指定级别
- Integer disableLevel = SaFoxUtil.getValueByType(value, int.class);
if(disableLevel >= level) {
throw new DisableServiceException(loginType, loginId, service, disableLevel, level, getDisableTime(loginId, service))
.setCode(SaErrorCode.CODE_11061);
@@ -2504,14 +2504,22 @@ public class StpLogic {
* @return /
*/
public int getDisableLevel(Object loginId, String service) {
- // 1、判断是否被封禁了,如果尚未被封禁,返回-2
+ // 1、先从缓存中查询数据,缓存中有值,以缓存值优先
String value = getSaTokenDao().get(splicingKeyDisable(loginId, service));
- if(SaFoxUtil.isEmpty(value)) {
- return SaTokenConsts.NOT_DISABLE_LEVEL;
+ if(SaFoxUtil.isNotEmpty(value)) {
+ return SaFoxUtil.getValueByType(value, int.class);
}
- // 2、转为 int 类型返回
- return SaFoxUtil.getValueByType(value, int.class);
+ // 2、如果缓存中无数据,则从"数据加载器"中再次查询
+ SaDisableWrapperInfo disableWrapperInfo = SaManager.getStpInterface().isDisabled(loginId, service);
+
+ // 如果返回值 disableTime 有效,则代表返回结果需要写入缓存
+ if(disableWrapperInfo.disableTime == SaTokenDao.NEVER_EXPIRE || disableWrapperInfo.disableTime > 0) {
+ disableLevel(loginId, service, disableWrapperInfo.disableLevel, disableWrapperInfo.disableTime);
+ }
+
+ // 返回查询结果
+ return disableWrapperInfo.disableLevel;
}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java
index 6d7a453d..af3f5b4a 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java
@@ -1,12 +1,11 @@
package com.pj.satoken;
+import cn.dev33.satoken.stp.StpInterface;
+import org.springframework.stereotype.Component;
+
import java.util.ArrayList;
import java.util.List;
-import org.springframework.stereotype.Component;
-
-import cn.dev33.satoken.stp.StpInterface;
-
/**
* 自定义权限认证接口扩展,Sa-Token 将从此实现类获取每个账号拥有的权限码
*