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