diff --git a/sa-token-doc/_sidebar.md b/sa-token-doc/_sidebar.md
index 3970e111..2f5c228a 100644
--- a/sa-token-doc/_sidebar.md
+++ b/sa-token-doc/_sidebar.md
@@ -132,6 +132,8 @@
- [解决反向代理 uri 丢失的问题](/fun/curr-domain)
- [解决跨域问题](/fun/cors-filter)
- [技术选型:SSO 与 OAuth2 对比](/fun/sso-vs-oauth2)
+ - [集成 MongoDB 参考一](/up/integ-spring-mongod-1)
+ - [集成 MongoDB 参考二](/up/integ-spring-mongod-2)
- [从 Shiro、SpringSecurity、JWT 迁移](/fun/auth-framework-function-test)
- [issue 提问模板](/fun/issue-template)
diff --git a/sa-token-doc/plugin/dao-extend.md b/sa-token-doc/plugin/dao-extend.md
index 96373f58..47bd78c3 100644
--- a/sa-token-doc/plugin/dao-extend.md
+++ b/sa-token-doc/plugin/dao-extend.md
@@ -19,10 +19,10 @@
有关 Redis 集成,详细参考:[集成Redis](/up/integ-redis),更多存储方式欢迎提交PR
-**扩展:**
-
-由 `@lilihao` 提供的 MongoDB 集成示例,参考:[集成 Spring MongodDB](/up/integ-spring-mongod)
+**扩展:集成 MongoDB**
+- [集成 MongoDB 参考一](/up/integ-spring-mongod-1)
+- [集成 MongoDB 参考二](/up/integ-spring-mongod-2)
diff --git a/sa-token-doc/up/integ-redis.md b/sa-token-doc/up/integ-redis.md
index 9265abda..a0d169f0 100644
--- a/sa-token-doc/up/integ-redis.md
+++ b/sa-token-doc/up/integ-redis.md
@@ -144,245 +144,6 @@ spring.redis.lettuce.pool.min-idle=0
**4. 集成包版本问题**
Sa-Token-Redis 集成包的版本尽量与 Sa-Token-Starter 集成包的版本一致,否则可能出现兼容性问题。
-### 示例:在 Spring Boot 下集成 MongoDB:
-
-
-
-``` xml
-
-
- org.springframework.boot
- spring-boot-starter-data-mongodb
-
-```
-
-``` gradle
-// 提供MongoDB依赖
-implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
-```
-
-
-1. 创建一个 `MySaSession` 并继承 `SaSession`
-```java
-public class MySaSession extends SaSession {
- public MySaSession(String id) {
- super(id);
- }
-
- public void setDataMap(Map 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()` 错误
-
-2. 在 `SpringBoot` 启动方法中重写 `SaStrategy.instance.createSession` 方法,使我们自定义的 `MySaSession` 生效
-```java
-@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);
- }
-}
-```
-
-3. 实现 SaTokenDao 接口
-```java
-//定义一个类用于保存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());
- }
-}
-```
-```java
-// SaTokenDao 实现
-@Component
-public class SaTokenDaoMongo implements SaTokenDao {
-
-
- private final MongoTemplate mongoTemplate;
-
- public SaTokenDaoMongo(MongoTemplate mongoTemplate) {
- this.mongoTemplate = mongoTemplate;
- }
-
-
- Optional 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 searchData(String prefix, String keyword, int start, int size, boolean sortType) {
-
- List wrapList = mongoTemplate.find(
- Query.query(Criteria.where("id").regex(prefix + "*" + keyword + "*").and("timeout").gte(new Date())),
- SaTokenWrap.class
- );
-
- List list = wrapList.stream().map(SaTokenWrap::getValue).filter(StringUtils::hasText).collect(Collectors.toList());
-
- return SaFoxUtil.searchList(list, start, size, sortType);
- }
-
-
-}
-```
@@ -390,5 +151,5 @@ public class SaTokenDaoMongo implements SaTokenDao {
### 扩展:集成 MongoDB
-由 `@lilihao` 提供的 MongoDB 集成示例,参考:[集成 Spring MongodDB](/up/integ-spring-mongod)
-
+- [集成 MongoDB 参考一](/up/integ-spring-mongod-1)
+- [集成 MongoDB 参考二](/up/integ-spring-mongod-2)
diff --git a/sa-token-doc/up/integ-spring-mongod.md b/sa-token-doc/up/integ-spring-mongod-1.md
similarity index 100%
rename from sa-token-doc/up/integ-spring-mongod.md
rename to sa-token-doc/up/integ-spring-mongod-1.md
diff --git a/sa-token-doc/up/integ-spring-mongod-2.md b/sa-token-doc/up/integ-spring-mongod-2.md
new file mode 100644
index 00000000..321af320
--- /dev/null
+++ b/sa-token-doc/up/integ-spring-mongod-2.md
@@ -0,0 +1,243 @@
+# Sa-Token 集成 MongoDB
+---
+
+在 Spring Boot 下集成 MongoDB:
+
+
+
+``` xml
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+```
+
+``` gradle
+// 提供MongoDB依赖
+implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
+```
+
+
+1. 创建一个 `MySaSession` 并继承 `SaSession`
+```java
+public class MySaSession extends SaSession {
+ public MySaSession(String id) {
+ super(id);
+ }
+
+ public void setDataMap(Map 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()` 错误
+
+2. 在 `SpringBoot` 启动方法中重写 `SaStrategy.instance.createSession` 方法,使我们自定义的 `MySaSession` 生效
+```java
+@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);
+ }
+}
+```
+
+3. 实现 SaTokenDao 接口
+```java
+//定义一个类用于保存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());
+ }
+}
+```
+```java
+// SaTokenDao 实现
+@Component
+public class SaTokenDaoMongo implements SaTokenDao {
+
+
+ private final MongoTemplate mongoTemplate;
+
+ public SaTokenDaoMongo(MongoTemplate mongoTemplate) {
+ this.mongoTemplate = mongoTemplate;
+ }
+
+
+ Optional 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 searchData(String prefix, String keyword, int start, int size, boolean sortType) {
+
+ List wrapList = mongoTemplate.find(
+ Query.query(Criteria.where("id").regex(prefix + "*" + keyword + "*").and("timeout").gte(new Date())),
+ SaTokenWrap.class
+ );
+
+ List list = wrapList.stream().map(SaTokenWrap::getValue).filter(StringUtils::hasText).collect(Collectors.toList());
+
+ return SaFoxUtil.searchList(list, start, size, sortType);
+ }
+
+
+}
+```
+