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 将从此实现类获取每个账号拥有的权限码 *