sa-token/sa-token-doc/up/integ-spring-mongod-2.md
2025-01-26 15:59:01 +08:00

6.6 KiB
Raw Blame History

Sa-Token 集成 MongoDB


在 Spring Boot 下集成 MongoDB

<!-- 提供MongoDB依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
// 提供MongoDB依赖
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
  1. 创建一个 MySaSession 并继承 SaSession
public class MySaSession extends SaSession {
    public MySaSession(String id) {
        super(id);
    }

    public void setDataMap(Map<String, Object> dataMap) {
        refreshDataMap(dataMap);
    }
}

原因:由于 SaSession 中的 dataMap 字段没有 setter 方法,当 spring-data-mongodb 反序列化 SaSession 时会报 Cannot set property dataMap because no setter, no wither and it's not part of the persistence constructor public cn.dev33.satoken.session.SaSession() 错误

  1. SpringBoot 启动方法中重写 SaStrategy.instance.createSession 方法,使我们自定义的 MySaSession 生效
@SpringBootApplication
public class SpringApplication {

    public static void main(String[] args) {

	// 重写 SaStrategy.instance.createSession 方法
        SaStrategy.instance.createSession = (sessionId) -> {
            return new MySaSession(sessionId);
        };

        SpringApplication.run(SpringApplication.class, args);
    }
}
  1. 实现 SaTokenDao 接口
//定义一个类用于保存SaSession
@Document
public class SaTokenWrap {
    private String id;
    private String value;
    private Object object;
    // 这里利用MongoDB的TTL索引当过期时MongoDB会自动删除过期的数据同时如果timeout如果为null那么视为永不删除
    @Indexed(expireAfterSeconds = 1, background = true)
    private Date timeout;


    public boolean live() {
        return getTimeout() == null || getTimeout().after(new Date());
    }
}
// SaTokenDao 实现
@Component
public class SaTokenDaoMongo implements SaTokenDao {


    private final MongoTemplate mongoTemplate;

    public SaTokenDaoMongo(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }


    Optional<SaTokenWrap> getByKey(String key) {
        SaTokenWrap tokenWrap = mongoTemplate.findById(key, SaTokenWrap.class);

        return Optional.ofNullable(tokenWrap).filter(SaTokenWrap::live);
    }

    Date timeoutToDate(long timeout) {

        return new Date(timeout * 1000 + System.currentTimeMillis());
    }

    void upsertByPath(String key, String path, Object value, long timeout) {
        if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
            return;
        }
        Update update = Update.update(path, value);

        if (timeout != SaTokenDao.NEVER_EXPIRE) {
            update.set("timeout", timeoutToDate(timeout));
        } else {
            update.unset("timeout");
        }

        mongoTemplate.upsert(
                Query.query(Criteria.where("id").is(key)),
                update,
                SaTokenWrap.class
        );
    }

    void updateByPath(String key, String path, Object value) {
        mongoTemplate.updateFirst(
                Query.query(Criteria.where("id").is(key).and("timeout").gte(new Date())),
                Update.update(path, value),
                SaTokenWrap.class
        );
    }

    // ------------------------ String 读写操作

    @Override
    public String get(String key) {

        return getByKey(key).map(SaTokenWrap::getValue).orElse(null);
    }

    @Override
    public void set(String key, String value, long timeout) {
        upsertByPath(key, "value", value, timeout);
    }

    @Override
    public void update(String key, String value) {
        updateByPath(key, "value", value);
    }

    @Override
    public void delete(String key) {
        mongoTemplate.remove(Query.query(Criteria.where("id").is(key)));
    }

    @Override
    public long getTimeout(String key) {

        SaTokenWrap tokenWrap = mongoTemplate.findById(key, SaTokenWrap.class);

        if (tokenWrap == null) {
            return SaTokenDao.NOT_VALUE_EXPIRE;
        }

        if (tokenWrap.getTimeout() == null) {
            return SaTokenDao.NEVER_EXPIRE;
        }

        long expire = tokenWrap.getTimeout().getTime();
        long timeout = (expire - System.currentTimeMillis()) / 1000;

        // 小于零时,视为不存在
        if (timeout < 0) {
            mongoTemplate.remove(Query.query(Criteria.where("id").is(key)));
            return SaTokenDao.NOT_VALUE_EXPIRE;
        }
        return timeout;
    }

    @Override
    public void updateTimeout(String key, long timeout) {

        Update update = new Update();

        if (timeout == SaTokenDao.NEVER_EXPIRE) {
            update.unset("timeout");
        } else {
            update.set("timeout", timeoutToDate(timeout));
        }

        mongoTemplate.upsert(
                Query.query(Criteria.where("id").is(key)),
                update,
                SaTokenWrap.class
        );
    }


    // ------------------------ Object 读写操作

    @Override
    public Object getObject(String key) {
        return getByKey(key).map(SaTokenWrap::getObject).orElse(null);
    }

    @Override
    public void setObject(String key, Object object, long timeout) {
        upsertByPath(key, "object", object, timeout);
    }

    @Override
    public void updateObject(String key, Object object) {
        updateByPath(key, "object", object);
    }

    @Override
    public void deleteObject(String key) {
        delete(key);
    }

    @Override
    public long getObjectTimeout(String key) {
        return getTimeout(key);
    }

    @Override
    public void updateObjectTimeout(String key, long timeout) {
        updateTimeout(key, timeout);
    }


    // ------------------------ Session 读写操作
    // 使用接口默认实现


    // --------- 会话管理

    @Override
    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {

        List<SaTokenWrap> wrapList = mongoTemplate.find(
                Query.query(Criteria.where("id").regex(prefix + "*" + keyword + "*").and("timeout").gte(new Date())),
                SaTokenWrap.class
        );

        List<String> list = wrapList.stream().map(SaTokenWrap::getValue).filter(StringUtils::hasText).collect(Collectors.toList());

        return SaFoxUtil.searchList(list, start, size, sortType);
    }


}