diff --git a/sa-token-demo/pom.xml b/sa-token-demo/pom.xml
index f068e233..428bcb90 100644
--- a/sa-token-demo/pom.xml
+++ b/sa-token-demo/pom.xml
@@ -57,8 +57,9 @@
sa-token-demo-webflux-springboot3
sa-token-demo-websocket
sa-token-demo-websocket-spring
+ sa-token-demo-loveqq-boot
-
+
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/pom.xml b/sa-token-demo/sa-token-demo-loveqq-boot/pom.xml
new file mode 100644
index 00000000..e544a063
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ com.kfyty
+ loveqq-framework
+ 1.1.2
+
+
+
+ sa-token-demo-loveqq-boot
+ 0.0.1-SNAPSHOT
+
+
+ 17
+ 17
+ 17
+ 17
+ 1.43.0
+
+
+
+
+
+ com.kfyty
+ loveqq-boot
+ ${loveqq.framework.version}
+
+
+
+
+ com.kfyty
+ loveqq-boot-starter-netty
+ ${loveqq.framework.version}
+
+
+
+
+ cn.dev33
+ sa-token-loveqq-boot-starter
+ ${sa-token.version}
+
+
+
+
+ com.kfyty
+ loveqq-boot-starter-logback
+ ${loveqq.framework.version}
+
+
+
+
+ org.yaml
+ snakeyaml
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/SaTokenLoveqqApplication.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/SaTokenLoveqqApplication.java
new file mode 100644
index 00000000..a43195ab
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/SaTokenLoveqqApplication.java
@@ -0,0 +1,21 @@
+package com.pj;
+
+import cn.dev33.satoken.SaManager;
+import com.kfyty.loveqq.framework.boot.K;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.BootApplication;
+import com.kfyty.loveqq.framework.web.core.autoconfig.annotation.EnableWebMvc;
+
+/**
+ * Sa-Token 整合 loveqq-framework 示例
+ *
+ * @author kfyty725
+ */
+@EnableWebMvc
+@BootApplication
+public class SaTokenLoveqqApplication {
+
+ public static void main(String[] args) {
+ K.run(SaTokenLoveqqApplication.class, args);
+ System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
+ }
+}
\ No newline at end of file
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/MyFilter.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/MyFilter.java
new file mode 100644
index 00000000..87da33ed
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/MyFilter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.pj.satoken;
+
+import cn.dev33.satoken.context.model.SaTokenContextModelBox;
+import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil;
+import cn.dev33.satoken.stp.StpUtil;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component;
+import com.kfyty.loveqq.framework.web.core.filter.Filter;
+import com.kfyty.loveqq.framework.web.core.filter.FilterChain;
+import com.kfyty.loveqq.framework.web.core.http.ServerRequest;
+import com.kfyty.loveqq.framework.web.core.http.ServerResponse;
+
+/**
+ * 自定义过滤器
+ */
+@Component
+public class MyFilter implements Filter {
+ /**
+ * 实现该方法,可以实现 servlet/reactor 的统一
+ * 但是该方法内部是同步方法,若需要异步,可以实现仅 reactor 支持的 {@link Filter#doFilter(ServerRequest, ServerResponse, FilterChain)} 方法
+ *
+ * @param request 请求
+ * @param response 响应
+ */
+ @Override
+ public Continue doFilter(ServerRequest request, ServerResponse response) {
+ System.out.println("进入自定义过滤器");
+
+ // 先 set 上下文,再调用 Sa-Token 同步 API,并在 finally 里清除上下文
+ SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response);
+ try {
+ System.out.println(StpUtil.isLogin());
+ } finally {
+ SaTokenContextUtil.clearContext(prev);
+ }
+
+ return Continue.TRUE;
+ }
+}
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/SaTokenConfigure.java
new file mode 100644
index 00000000..03f88147
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/SaTokenConfigure.java
@@ -0,0 +1,39 @@
+package com.pj.satoken;
+
+import cn.dev33.satoken.loveqq.boot.filter.SaRequestFilter;
+import cn.dev33.satoken.util.SaResult;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Configuration;
+
+/**
+ * [Sa-Token 权限认证] 配置类
+ * @author click33
+ *
+ */
+@Configuration
+public class SaTokenConfigure {
+
+ /**
+ * 注册 [sa-token全局过滤器]
+ */
+ @Bean
+ public SaRequestFilter getSaReactorFilter() {
+ return new SaRequestFilter()
+ // 指定 [拦截路由]
+ .addInclude("/**")
+ // 指定 [放行路由]
+ .addExclude("/favicon.ico")
+ // 指定[认证函数]: 每次请求执行
+ .setAuth(r -> {
+ System.out.println("---------- sa全局认证");
+ // SaRouter.match("/test/test", () -> StpUtil.checkLogin());
+ })
+ // 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
+ .setError(e -> {
+ System.out.println("---------- sa全局异常 ");
+ e.printStackTrace();
+ return SaResult.error(e.getMessage());
+ })
+ ;
+ }
+}
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/StpInterfaceImpl.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/StpInterfaceImpl.java
new file mode 100644
index 00000000..47361274
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/StpInterfaceImpl.java
@@ -0,0 +1,42 @@
+package com.pj.satoken;
+
+import cn.dev33.satoken.stp.StpInterface;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 自定义权限验证接口扩展
+ */
+@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
+public class StpInterfaceImpl implements StpInterface {
+
+ /**
+ * 返回一个账号所拥有的权限码集合
+ */
+ @Override
+ public List getPermissionList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
+ List list = new ArrayList();
+ list.add("101");
+ list.add("user-add");
+ list.add("user-delete");
+ list.add("user-update");
+ list.add("user-get");
+ list.add("article-get");
+ return list;
+ }
+
+ /**
+ * 返回一个账号所拥有的角色标识集合
+ */
+ @Override
+ public List getRoleList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
+ List list = new ArrayList();
+ list.add("admin");
+ list.add("super-admin");
+ return list;
+ }
+}
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/GlobalException.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/GlobalException.java
new file mode 100644
index 00000000..9dc5b51b
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/GlobalException.java
@@ -0,0 +1,18 @@
+package com.pj.test;
+
+import cn.dev33.satoken.util.SaResult;
+import com.kfyty.loveqq.framework.web.core.annotation.ExceptionHandler;
+import com.kfyty.loveqq.framework.web.core.annotation.RestControllerAdvice;
+
+/**
+ * 全局异常处理
+ */
+@RestControllerAdvice
+public class GlobalException {
+
+ @ExceptionHandler
+ public SaResult handlerException(Exception e) {
+ e.printStackTrace();
+ return SaResult.error(e.getMessage());
+ }
+}
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/TestController.java
new file mode 100644
index 00000000..48e5b4fc
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/TestController.java
@@ -0,0 +1,115 @@
+package com.pj.test;
+
+import cn.dev33.satoken.loveqq.boot.context.SaReactorHolder;
+import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired;
+import com.kfyty.loveqq.framework.web.core.annotation.GetMapping;
+import com.kfyty.loveqq.framework.web.core.annotation.RequestMapping;
+import com.kfyty.loveqq.framework.web.core.annotation.RestController;
+import com.kfyty.loveqq.framework.web.core.annotation.bind.CookieValue;
+import com.kfyty.loveqq.framework.web.core.annotation.bind.RequestParam;
+import com.kfyty.loveqq.framework.web.core.http.ServerRequest;
+import com.kfyty.loveqq.framework.web.core.http.ServerResponse;
+import reactor.core.publisher.Mono;
+
+import java.time.Duration;
+
+/**
+ * 测试专用Controller
+ *
+ * @author click33
+ */
+@RestController
+@RequestMapping("/test/")
+public class TestController {
+
+ @Autowired
+ UserService userService;
+
+ // 登录测试:Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
+ @GetMapping("login")
+ public Mono login(@RequestParam(defaultValue = "10001") String id) {
+ return SaReactorHolder.sync(() -> {
+ StpUtil.login(id);
+ return SaResult.ok("登录成功");
+ });
+ }
+
+ // API测试:手动设置上下文、try-finally 形式 --- http://localhost:8081/test/isLogin
+ @GetMapping("isLogin")
+ public SaResult isLogin(ServerRequest request, ServerResponse response) {
+ try {
+ SaTokenContextUtil.setContext(request, response);
+ System.out.println("是否登录:" + StpUtil.isLogin());
+ return SaResult.data(StpUtil.getTokenInfo());
+ } finally {
+ SaTokenContextUtil.clearContext(null);
+ }
+ }
+
+ // API测试:手动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin2
+ @GetMapping("isLogin2")
+ public SaResult isLogin2(ServerRequest request, ServerResponse response) {
+ SaResult res = SaTokenContextUtil.setContext(request, response, () -> {
+ System.out.println("是否登录:" + StpUtil.isLogin());
+ return SaResult.data(StpUtil.getTokenInfo());
+ });
+ return SaResult.data(res);
+ }
+
+ // API测试:自动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin3
+ @GetMapping("isLogin3")
+ public Mono isLogin3() {
+ return SaReactorHolder.sync(() -> {
+ System.out.println("是否登录:" + StpUtil.isLogin());
+ userService.isLogin();
+ return SaResult.data(StpUtil.getTokenInfo());
+ });
+ }
+
+ // API测试:自动设置上下文、调用 userService Mono 方法 --- http://localhost:8081/test/isLogin4
+ @GetMapping("isLogin4")
+ public Mono isLogin4() {
+ return userService.findUserIdByNamePwd("ZhangSan", "123456")
+ .flatMap(userId -> SaReactorHolder.sync(() -> {
+ StpUtil.login(userId);
+ return SaResult.data(StpUtil.getTokenInfo());
+ }));
+ }
+
+ // API测试:切换线程、复杂嵌套调用 --- http://localhost:8081/test/isLogin5
+ @GetMapping("isLogin5")
+ public Mono isLogin5() {
+ System.out.println("线程id-----" + Thread.currentThread().getId());
+ // 要点:在流里调用 Sa-Token API 之前,必须用 SaReactorHolder.sync( () -> {} ) 进行包裹
+ return Mono.delay(Duration.ofSeconds(1))
+ .doOnNext(r -> System.out.println("线程id-----" + Thread.currentThread().getId()))
+ .map(r -> SaReactorHolder.sync(() -> userService.isLogin()))
+ .map(r -> userService.findUserIdByNamePwd("ZhangSan", "123456"))
+ .map(r -> SaReactorHolder.sync(() -> userService.isLogin()))
+ .flatMap(isLogin -> {
+ System.out.println("是否登录 " + isLogin);
+ return SaReactorHolder.sync(() -> {
+ System.out.println("是否登录 " + StpUtil.isLogin());
+ return SaResult.data(StpUtil.getTokenInfo());
+ });
+ });
+ }
+
+ // API测试:使用上下文无关的API --- http://localhost:8081/test/isLogin6
+ @GetMapping("isLogin6")
+ public SaResult isLogin6(@CookieValue("satoken") String satoken) {
+ System.out.println("token 为:" + satoken);
+ System.out.println("登录人:" + StpUtil.getLoginIdByToken(satoken));
+ return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
+ }
+
+ // 测试 浏览器访问: http://localhost:8081/test/test
+ @GetMapping("test")
+ public SaResult test() {
+ System.out.println("线程id------- " + Thread.currentThread().getId());
+ return SaResult.ok();
+ }
+}
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/UserService.java b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/UserService.java
new file mode 100644
index 00000000..65fbec05
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/UserService.java
@@ -0,0 +1,24 @@
+package com.pj.test;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.kfyty.loveqq.framework.core.autoconfig.annotation.Service;
+import reactor.core.publisher.Mono;
+
+/**
+ * 模拟 Service 方法
+ * @author click33
+ * @since 2025/4/6
+ */
+@Service
+public class UserService {
+
+ public boolean isLogin() {
+ System.out.println("UserService 里调用 API 测试,是否登录:" + StpUtil.isLogin());
+ return StpUtil.isLogin();
+ }
+
+ public Mono findUserIdByNamePwd(String name, String pwd) {
+ // ...
+ return Mono.just(10001L);
+ }
+}
\ No newline at end of file
diff --git a/sa-token-demo/sa-token-demo-loveqq-boot/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/resources/application.yml
new file mode 100644
index 00000000..7babd805
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-loveqq-boot/src/main/resources/application.yml
@@ -0,0 +1,4 @@
+# 端口
+k:
+ server:
+ port: 8081