Sa-Token源码学习

秋招结束 准备学习开源代码提升自己

Sa-token:https://github.com/dromara/Sa-Token?tab=readme-ov-file

模型解析

传统HttpSession:客户端在cookie中保存sessionId,到服务端去查询,因此两个客户端登陆相同账号分配不同的sessionId。

Sa-token:以账号为维度,仅登陆时分配,而并非握手时分配。

地址如下:https://sa-token.cc/doc.html#/fun/session-model

以下是核心源码解析

读取配置

读取配置的代码统一收口

public SaTokenConfig getConfigOrGlobal() {
        SaTokenConfig cfg = getConfig();
        if(cfg != null) {
            return cfg;
        }
        return SaManager.getConfig();
    }

单例实现配置只读取一次,且唯一

public static SaTokenConfig getConfig() {
        if (config == null) {
            synchronized (SaManager.class) {
                if (config == null) {
                    setConfigMethod(SaTokenConfigFactory.createConfig());
                }
            }
        }
        return config;
    }

从配置文件的路径读取转化成Map

private static Map<String, String> readPropToMap(String propertiesPath) {
        Map<String, String> map = new HashMap<>(16);
        try {
            InputStream is = SaTokenConfigFactory.class.getClassLoader().getResourceAsStream(propertiesPath);
            if (is == null) {
                return null;
            }
            Properties prop = new Properties();
            prop.load(is);
            for (String key : prop.stringPropertyNames()) {
                map.put(key, prop.getProperty(key));
            }
        } catch (IOException e) {
            throw new SaTokenException("配置文件(" + propertiesPath + ")加载失败", e).setCode(SaErrorCode.CODE_10021);
        }
        return map;
    }

反射创建Config对象

private static Object initPropByMap(Map<String, String> map, Object obj) {

        if (map == null) {
            map = new HashMap<>(16);
        }

        // 1、取出类型
        Class<?> cs;
        if (obj instanceof Class) {
            // 如果是一个类型,则将obj=null,以便完成静态属性反射赋值
            cs = (Class<?>) obj;
            obj = null;
        } else {
            // 如果是一个对象,则取出其类型
            cs = obj.getClass();
        }

        // 2、遍历类型属性,反射赋值
        for (Field field : cs.getDeclaredFields()) {
            String value = map.get(field.getName());
            if (value == null) {
                // 如果为空代表没有配置此项
                continue;
            }
            try {
                Object valueConvert = SaFoxUtil.getValueByType(value, field.getType());
                field.setAccessible(true);
                field.set(obj, valueConvert);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new SaTokenException("属性赋值出错:" + field.getName(), e).setCode(SaErrorCode.CODE_10022);
            }
        }
        return obj;
    }

会话的操作

session的写入与读取在SaTokenDao接口中获取,根据实现策略不同(例如存储在redis中),有15种不同的实现类。

/**
* 获取 SaSession,如无返空
* @param sessionId sessionId
* @return SaSession
*/
default SaSession getSession(String sessionId) {
    return (SaSession)getObject(sessionId);
}

/**
* 写入 SaSession,并设定存活时间(单位: 秒)
* @param session 要保存的 SaSession 对象
* @param timeout 过期时间(单位: 秒)
*/
default void setSession(SaSession session, long timeout) {
    setObject(session.getId(), session, timeout);
}

生成唯一token的算法如下:

// 4、如果代码走到此处,说明未能成功复用旧 token,需要根据算法新建 token
return SaStrategy.instance.generateUniqueToken.execute(
        "token",
        getConfigOfMaxTryTimes(),
        () -> {
            return createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout(), loginModel.getExtraData());
        },
        tokenValue -> {
            return getLoginIdNotHandle(tokenValue) == null;
        }
);

/**
* 生成唯一式 token 的算法
*/
public SaGenerateUniqueTokenFunction generateUniqueToken = (elementName, maxTryTimes, createTokenFunction, checkTokenFunction) -> {

    // 为方便叙述,以下代码注释均假设在处理生成 token 的场景,但实际上本方法也可能被用于生成 code、ticket 等

    // 循环生成
    for (int i = 1; ; i++) {
        // 生成 token
        String token = createTokenFunction.get();

        // 如果 maxTryTimes == -1,表示不做唯一性验证,直接返回
        if (maxTryTimes == -1) {
            return token;
        }

        // 如果 token 在DB库查询不到数据,说明是个可用的全新 token,直接返回
        if (checkTokenFunction.apply(token)) {
            return token;
        }

        // 如果已经循环了 maxTryTimes 次,仍然没有创建出可用的 token,那么抛出异常
        if (i >= maxTryTimes) {
            throw new SaTokenException(elementName + " 生成失败,已尝试" + i + "次,生成算法过于简单或资源池已耗尽");
        }
    }
};

监听器

每次用户在执行创建会话时均会调取监听中心的方法,注册事件,是硬编码写在代码中的,并非注解方式,如下:

public SaSession(String id) {
    this.id = id;
    this.createTime = System.currentTimeMillis();
    // $$ 发布事件
    SaTokenEventCenter.doCreateSession(id);
}
感谢您的收看~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇