探索 Sa-Token (五)SaManager # SaTokenConfig 全局组件设计原理

SaManager 管理 Sa-Token 所有全局组件,可通过此类快速获取、写入各种全局组件对象

其实也就是框架的context上下文,可以获取到全局组件信息,那么就以为 # SaTokenConfig 为例子说下框架中全局组件如何设计。

Springboot 自动装配

定义在META-INF下的spring.factories中的类全路径,spring会自动帮我们扫描

那么按照框架中的设计,依葫芦画瓢我们也整一个。

 配置类 # SaBeanRegister

package com.example.demo.satoken.spirng;

import com.example.demo.satoken.config.SaTokenConfigPlus;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
 * @author Jerry
 * @date 2024-01-23 11:19
 * 注册Sa-Token所需要的Bean
 * <p> Bean 的注册与注入应该分开在两个文件中,否则在某些场景下会造成循环依赖
 */
public class SaBeanRegister {

    /**
     * 获取配置Bean
     *
     * @return 配置对象
     */
    @Bean
    @ConfigurationProperties(prefix = "sa-token-plus")
    public SaTokenConfigPlus getSaTokenConfigPlus() {
        return new SaTokenConfigPlus();
    }
}

可以看到这里注入一个Bean # SaTokenConfigPlus 没错就是我们自定义的类,属性可以配置在Yml文件中,

配置类: # SaTokenConfigPlus

package com.example.demo.satoken.config;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Jerry
 * @date 2024-01-23 11:00
 * 自定义配置
 */
@Data
public class SaTokenConfigPlus implements Serializable {


    private static final long serialVersionUID = -6541180061782004705L;

    /** token名称 (同时也是cookie名称) */
    private String tokenName = "satoken";

    /** token的长久有效期(单位:秒) 默认30天, -1代表永久 */
    private long timeout = 60 * 60 * 24 * 30;

    /**
     * token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
     * (例如可以设置为1800代表30分钟内无操作就过期)
     */
    private long activityTimeout = -1;

    /** 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) */
    private Boolean isConcurrent = true;

    /** 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) */
    private Boolean isShare = true;

    /**
     * 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置才有效)
     */
    private int maxLoginCount = 12;

    /** 是否尝试从请求体里读取token */
    private Boolean isReadBody = true;

    /** 是否尝试从header里读取token */
    private Boolean isReadHead = true;

    /** 是否尝试从cookie里读取token */
    private Boolean isReadCookie = true;

    /** token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) */
    private String tokenStyle = "uuid";

    /** 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒) ,默认值30秒,设置为-1代表不启动定时清理 */
    private int dataRefreshPeriod = 30;

    /** 获取[token专属session]时是否必须登录 (如果配置为true,会在每次获取[token-session]时校验是否登录) */
    private Boolean tokenSessionCheckLogin = true;

    /** 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作)  */
    private Boolean autoRenew = true;

    /** token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx) */
    private String tokenPrefix;

    /** 是否在初始化配置时打印版本字符画 */
    private Boolean isPrint = true;

    /** 是否打印操作日志 */
    private Boolean isLog = false;

    /**
     * jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
     */
    private String jwtSecretKey;

    /**
     * Id-Token的有效期 (单位: 秒)
     */
    private long idTokenTimeout = 60 * 60 * 24;

    /**
     * Http Basic 认证的账号和密码
     */
    private String basic = "";

    /** 配置当前项目的网络访问地址 */
    private String currDomain;

    /** 是否校验Id-Token(部分rpc插件有效) */
    private Boolean checkIdToken = false;

}

其实这里字段从sa-token中copy过来的,然后我们就可以在yml中定义自己的配置信息

可以看到idea还会提示我们自定义的配置字段信息

注册到全局组件 

配置类 #SaBeanInjectPlus 这个类也是boot自动装配中定义

package com.example.demo.satoken.spirng;

import cn.dev33.satoken.stp.StpInterface;
import com.example.demo.satoken.config.SaManagerPlus;
import com.example.demo.satoken.config.SaTokenConfigPlus;
import com.example.demo.satoken.listener.SaTokenEventCenterPlus;
import com.example.demo.satoken.listener.SaTokenListenerPlus;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author Jerry
 * @date 2024-01-23 11:00
 * 注入 Sa-Token 所需要的 Bean
 */
public class SaBeanInjectPlus {


    public SaBeanInjectPlus(@Autowired(required = false) SaTokenConfigPlus saTokenConfig) {
        if (saTokenConfig != null) {
            SaManagerPlus.setConfig(saTokenConfig);
        }
    }

 

}

全局组件 #  SaManagerPlus

package com.example.demo.satoken.config;

import cn.dev33.satoken.config.SaTokenConfigFactory;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;

/**
 * @author yueF_L
 * @version 1.0
 * @date 2022-09-08 11:52
 * <p>
 * 管理 Sa-Token 所有全局组
 * 自定义测试
 */
public class SaManagerPlus {


    private volatile static SaTokenConfigPlus configPlus;

    public static void setConfig(SaTokenConfigPlus saTokenConfigPlus) {
        setConfigPlus(saTokenConfigPlus);
    }

    private static void setConfigPlus(SaTokenConfigPlus configPlus) {
        SaManagerPlus.configPlus = configPlus;
    }

    /**
     * 获取 Sa-Token 的全局配置信息
     *
     * @return 全局配置信息
     */
    public static SaTokenConfigPlus getConfigPlus() {
        if (configPlus == null) {
            synchronized (SaManagerPlus.class) {
                if (configPlus == null) {
                    // 给默认实现
                    //setConfigMethod(SaTokenConfigFactory.createConfig());
                }
            }
        }
        return configPlus;
    }


    private volatile static StpInterface stpInterface;

    public static void setStpInterface(StpInterface stpInterface) {
        SaManagerPlus.stpInterface = stpInterface;
    }

    public static StpInterface getStpInterface() {
        if (stpInterface == null) {
            synchronized (SaManagerPlus.class) {
                if (stpInterface == null) {
                    setStpInterface(new StpInterfaceDefaultImpl());
                }
            }
        }
        return stpInterface;
    }
}

采用双重校验锁的单例模式

验证: