From f4e7dac95fedcfe938e987dfd9cf348ab92d4ebb Mon Sep 17 00:00:00 2001 From: kfyty725 Date: Tue, 3 Jun 2025 17:00:14 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:loveqq-framework=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-dependencies/pom.xml | 22 ++ sa-token-starter/pom.xml | 1 + .../sa-token-loveqq-boot-starter/pom.xml | 84 ++++++ .../satoken/loveqq/boot/SaBeanInject.java | 275 ++++++++++++++++++ .../satoken/loveqq/boot/SaBeanRegister.java | 69 +++++ .../boot/apiKey/SaApiKeyBeanInject.java | 64 ++++ .../boot/apiKey/SaApiKeyBeanRegister.java | 43 +++ .../loveqq/boot/context/SaReactorHolder.java | 68 +++++ .../path/ApplicationContextPathLoading.java | 52 ++++ .../boot/filter/SaFirewallCheckFilter.java | 72 +++++ .../loveqq/boot/filter/SaRequestFilter.java | 151 ++++++++++ .../boot/filter/SaTokenContextFilter.java | 42 +++ .../loveqq/boot/filter/SaTokenCorsFilter.java | 58 ++++ .../boot/interceptor/SaInterceptor.java | 139 +++++++++ .../loveqq/boot/model/LoveqqSaRequest.java | 116 ++++++++ .../loveqq/boot/model/LoveqqSaResponse.java | 48 +++ .../loveqq/boot/model/LoveqqSaStorage.java | 42 +++ .../boot/oauth2/SaOAuth2BeanInject.java | 153 ++++++++++ .../boot/oauth2/SaOAuth2BeanRegister.java | 43 +++ .../satoken/loveqq/boot/package-info.java | 19 ++ .../loveqq/boot/sign/SaSignBeanInject.java | 64 ++++ .../loveqq/boot/sign/SaSignBeanRegister.java | 55 ++++ .../loveqq/boot/sso/SaSsoBeanInject.java | 77 +++++ .../loveqq/boot/sso/SaSsoBeanRegister.java | 82 ++++++ .../boot/support/SaPathMatcherHolder.java | 68 +++++ .../loveqq/boot/utils/SaTokenContextUtil.java | 125 ++++++++ .../loveqq/boot/utils/SaTokenOperateUtil.java | 53 ++++ .../src/main/resources/META-INF/k.factories | 11 + 28 files changed, 2096 insertions(+) create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/pom.xml create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanInject.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanRegister.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanInject.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanRegister.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/SaReactorHolder.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/path/ApplicationContextPathLoading.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/interceptor/SaInterceptor.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaRequest.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaResponse.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaStorage.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanInject.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanRegister.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/package-info.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanInject.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanRegister.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanInject.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanRegister.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/support/SaPathMatcherHolder.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenOperateUtil.java create mode 100644 sa-token-starter/sa-token-loveqq-boot-starter/src/main/resources/META-INF/k.factories diff --git a/sa-token-dependencies/pom.xml b/sa-token-dependencies/pom.xml index 5f897730..254a0fa3 100644 --- a/sa-token-dependencies/pom.xml +++ b/sa-token-dependencies/pom.xml @@ -30,6 +30,7 @@ 3.2.133 4.9.17 3.14.4 + 1.1.2 2.5.0 2.7.21 2.10.1.RELEASE @@ -124,6 +125,27 @@ jfinal ${jfinal.version} + + + + com.kfyty + loveqq-core + ${loveqq.version} + + + + + com.kfyty + loveqq-mvc-core + ${loveqq.version} + + + + + com.kfyty + loveqq-boot-starter-redisson + ${loveqq.version} + + + com.kfyty + loveqq-core + + + + + com.kfyty + loveqq-mvc-core + provided + + + + + cn.dev33 + sa-token-jackson + + + + + com.kfyty + loveqq-boot-starter-redisson + provided + + + + + jakarta.servlet + jakarta.servlet-api + true + + + + + cn.dev33 + sa-token-sso + true + + + + + cn.dev33 + sa-token-oauth2 + true + + + + + cn.dev33 + sa-token-apikey + true + + + + + cn.dev33 + sa-token-sign + true + + + \ No newline at end of file diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanInject.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanInject.java new file mode 100644 index 00000000..a7595813 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanInject.java @@ -0,0 +1,275 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.context.SaTokenContext; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction; +import cn.dev33.satoken.http.SaHttpTemplate; +import cn.dev33.satoken.httpauth.basic.SaHttpBasicTemplate; +import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil; +import cn.dev33.satoken.httpauth.digest.SaHttpDigestTemplate; +import cn.dev33.satoken.httpauth.digest.SaHttpDigestUtil; +import cn.dev33.satoken.json.SaJsonTemplate; +import cn.dev33.satoken.listener.SaTokenEventCenter; +import cn.dev33.satoken.listener.SaTokenListener; +import cn.dev33.satoken.log.SaLog; +import cn.dev33.satoken.loveqq.boot.support.SaPathMatcherHolder; +import cn.dev33.satoken.plugin.SaTokenPlugin; +import cn.dev33.satoken.plugin.SaTokenPluginHolder; +import cn.dev33.satoken.same.SaSameTemplate; +import cn.dev33.satoken.secure.totp.SaTotpTemplate; +import cn.dev33.satoken.serializer.SaSerializerTemplate; +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.strategy.SaAnnotationStrategy; +import cn.dev33.satoken.strategy.SaFirewallStrategy; +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.strategy.hooks.SaFirewallCheckHook; +import cn.dev33.satoken.temp.SaTempTemplate; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.support.PatternMatcher; + +import java.util.List; + +/** + * 注入 Sa-Token 所需要的 Bean + * + * @author click33 + * @since 1.34.0 + */ +@Component +public class SaBeanInject { + + /** + * 组件注入 + *

为确保 Log 组件正常打印,必须将 SaLog 和 SaTokenConfig 率先初始化

+ * + * @param log log 对象 + * @param saTokenConfig 配置对象 + */ + public SaBeanInject(@Autowired(required = false) SaLog log, + @Autowired(required = false) SaTokenConfig saTokenConfig, + @Autowired(required = false) SaTokenPluginHolder pluginHolder) { + if (log != null) { + SaManager.setLog(log); + } + + if (saTokenConfig != null) { + SaManager.setConfig(saTokenConfig); + } + + // 初始化 Sa-Token SPI 插件 + if (pluginHolder == null) { + pluginHolder = SaTokenPluginHolder.instance; + } + + pluginHolder.init(); + + SaTokenPluginHolder.instance = pluginHolder; + } + + /** + * 注入持久化Bean + * + * @param saTokenDao SaTokenDao对象 + */ + @Autowired(required = false) + public void setSaTokenDao(SaTokenDao saTokenDao) { + SaManager.setSaTokenDao(saTokenDao); + } + + /** + * 注入权限认证Bean + * + * @param stpInterface StpInterface对象 + */ + @Autowired(required = false) + public void setStpInterface(StpInterface stpInterface) { + SaManager.setStpInterface(stpInterface); + } + + /** + * 注入上下文Bean + * + * @param saTokenContext SaTokenContext对象 + */ + @Autowired(required = false) + public void setSaTokenContext(SaTokenContext saTokenContext) { + SaManager.setSaTokenContext(saTokenContext); + } + + /** + * 注入侦听器Bean + * + * @param listenerList 侦听器集合 + */ + @Autowired(required = false) + public void setSaTokenListener(List listenerList) { + SaTokenEventCenter.registerListenerList(listenerList); + } + + /** + * 注入自定义注解处理器 + * + * @param handlerList 自定义注解处理器集合 + */ + @Autowired(required = false) + public void setSaAnnotationHandler(List> handlerList) { + for (SaAnnotationHandlerInterface handler : handlerList) { + SaAnnotationStrategy.instance.registerAnnotationHandler(handler); + } + } + + /** + * 注入临时令牌验证模块 Bean + * + * @param saTempTemplate / + */ + @Autowired(required = false) + public void setSaTempTemplate(SaTempTemplate saTempTemplate) { + SaManager.setSaTempTemplate(saTempTemplate); + } + + /** + * 注入 Same-Token 模块 Bean + * + * @param saSameTemplate saSameTemplate对象 + */ + @Autowired(required = false) + public void setSaIdTemplate(SaSameTemplate saSameTemplate) { + SaManager.setSaSameTemplate(saSameTemplate); + } + + /** + * 注入 Sa-Token Http Basic 认证模块 + * + * @param saBasicTemplate saBasicTemplate对象 + */ + @Autowired(required = false) + public void setSaHttpBasicTemplate(SaHttpBasicTemplate saBasicTemplate) { + SaHttpBasicUtil.saHttpBasicTemplate = saBasicTemplate; + } + + /** + * 注入 Sa-Token Http Digest 认证模块 + * + * @param saHttpDigestTemplate saHttpDigestTemplate 对象 + */ + @Autowired(required = false) + public void setSaHttpDigestTemplate(SaHttpDigestTemplate saHttpDigestTemplate) { + SaHttpDigestUtil.saHttpDigestTemplate = saHttpDigestTemplate; + } + + /** + * 注入自定义的 JSON 转换器 Bean + * + * @param saJsonTemplate JSON 转换器 + */ + @Autowired(required = false) + public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) { + SaManager.setSaJsonTemplate(saJsonTemplate); + } + + /** + * 注入自定义的 Http 转换器 Bean + * + * @param saHttpTemplate / + */ + @Autowired(required = false) + public void setSaHttpTemplate(SaHttpTemplate saHttpTemplate) { + SaManager.setSaHttpTemplate(saHttpTemplate); + } + + /** + * 注入自定义的序列化器 Bean + * + * @param saSerializerTemplate 序列化器 + */ + @Autowired(required = false) + public void setSaSerializerTemplate(SaSerializerTemplate saSerializerTemplate) { + SaManager.setSaSerializerTemplate(saSerializerTemplate); + } + + /** + * 注入自定义的 TOTP 算法 Bean + * + * @param totpTemplate TOTP 算法类 + */ + @Autowired(required = false) + public void setSaTotpTemplate(SaTotpTemplate totpTemplate) { + SaManager.setSaTotpTemplate(totpTemplate); + } + + /** + * 注入自定义的 StpLogic + * + * @param stpLogic / + */ + @Autowired(required = false) + public void setStpLogic(StpLogic stpLogic) { + StpUtil.setStpLogic(stpLogic); + } + + /** + * 利用自动注入特性,获取Spring框架内部使用的路由匹配器 + * + * @param pathMatcher 要设置的 pathMatcher + */ + @Autowired(required = false) + public void setPathMatcher(PatternMatcher pathMatcher) { + SaPathMatcherHolder.setPathMatcher(pathMatcher); + } + + /** + * 注入自定义防火墙校验 hook 集合 + * + * @param hooks / + */ + @Autowired(required = false) + public void setSaFirewallCheckHooks(List hooks) { + for (SaFirewallCheckHook hook : hooks) { + SaFirewallStrategy.instance.registerHook(hook); + } + } + + /** + * 注入CORS 策略处理函数 + * + * @param corsHandle / + */ + @Autowired(required = false) + public void setCorsHandle(SaCorsHandleFunction corsHandle) { + SaStrategy.instance.corsHandle = corsHandle; + } + + /** + * 注入自定义插件集合 + * + * @param plugins / + */ + @Autowired(required = false) + public void setSaTokenPluginList(List plugins) { + for (SaTokenPlugin plugin : plugins) { + SaTokenPluginHolder.instance.installPlugin(plugin); + } + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanRegister.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanRegister.java new file mode 100644 index 00000000..7b18256f --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/SaBeanRegister.java @@ -0,0 +1,69 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot; + +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.loveqq.boot.context.path.ApplicationContextPathLoading; +import cn.dev33.satoken.loveqq.boot.filter.SaFirewallCheckFilter; +import cn.dev33.satoken.loveqq.boot.filter.SaTokenContextFilter; +import cn.dev33.satoken.loveqq.boot.filter.SaTokenCorsFilter; +import cn.dev33.satoken.loveqq.boot.support.SaPathMatcherHolder; +import cn.dev33.satoken.strategy.SaStrategy; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.ConfigurationProperties; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Import; + +/** + * 注册Sa-Token所需要的Bean + *

Bean 的注册与注入应该分开在两个文件中,否则在某些场景下会造成循环依赖 + * + * @author click33 + */ +@Component +@Import(config = { + SaFirewallCheckFilter.class, + SaTokenContextFilter.class, + SaTokenCorsFilter.class +}) +public class SaBeanRegister { + + public SaBeanRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = SaPathMatcherHolder::match; + } + + /** + * 获取配置Bean + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token") + public SaTokenConfig getSaTokenConfig() { + return new SaTokenConfig(); + } + + /** + * 应用上下文路径加载器 + * + * @return / + */ + @Bean + public ApplicationContextPathLoading getApplicationContextPathLoading() { + return new ApplicationContextPathLoading(); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanInject.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanInject.java new file mode 100644 index 00000000..369b1467 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanInject.java @@ -0,0 +1,64 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.apiKey; + +import cn.dev33.satoken.apikey.SaApiKeyManager; +import cn.dev33.satoken.apikey.config.SaApiKeyConfig; +import cn.dev33.satoken.apikey.loader.SaApiKeyDataLoader; +import cn.dev33.satoken.apikey.template.SaApiKeyTemplate; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注入 Sa-Token API Key 所需要的 Bean + * + * @author click33 + * @since 1.43.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.apikey.SaApiKeyManager") +public class SaApiKeyBeanInject { + /** + * 注入 API Key 配置对象 + * + * @param saApiKeyConfig 配置对象 + */ + @Autowired(required = false) + public void setSaApiKeyConfig(SaApiKeyConfig saApiKeyConfig) { + SaApiKeyManager.setConfig(saApiKeyConfig); + } + + /** + * 注入自定义的 API Key 模版方法 Bean + * + * @param apiKeyTemplate / + */ + @Autowired(required = false) + public void setSaApiKeyTemplate(SaApiKeyTemplate apiKeyTemplate) { + SaApiKeyManager.setSaApiKeyTemplate(apiKeyTemplate); + } + + /** + * 注入自定义的 API Key 数据加载器 Bean + * + * @param apiKeyDataLoader / + */ + @Autowired(required = false) + public void setSaApiKeyDataLoader(SaApiKeyDataLoader apiKeyDataLoader) { + SaApiKeyManager.setSaApiKeyDataLoader(apiKeyDataLoader); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanRegister.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanRegister.java new file mode 100644 index 00000000..20a625f4 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/apiKey/SaApiKeyBeanRegister.java @@ -0,0 +1,43 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.apiKey; + +import cn.dev33.satoken.apikey.config.SaApiKeyConfig; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.ConfigurationProperties; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注册 Sa-Token API Key 所需要的 Bean + * + * @author click33 + * @since 1.43.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.apikey.SaApiKeyManager") +public class SaApiKeyBeanRegister { + /** + * 获取 API Key 配置对象 + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token.api-key") + public SaApiKeyConfig getSaApiKeyConfig() { + return new SaApiKeyConfig(); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/SaReactorHolder.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/SaReactorHolder.java new file mode 100644 index 00000000..82bd87b8 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/SaReactorHolder.java @@ -0,0 +1,68 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.context; + +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.fun.SaRetGenericFunction; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; +import reactor.core.publisher.Mono; + +/** + * Reactor 上下文操作(异步),持有当前请求的 ServerWebExchange 全局引用 + * + * @author click33 + * @since 1.19.0 + */ +public class SaReactorHolder { + public static final String REQUEST_CONTEXT_ATTRIBUTE = "com.kfyty.loveqq.framework.web.mvc.netty.request.support.RequestContextHolder.REQUEST_CONTEXT_ATTRIBUTE"; + public static final String RESPONSE_CONTEXT_ATTRIBUTE = "com.kfyty.loveqq.framework.web.mvc.netty.request.support.ResponseContextHolder.REQUEST_CONTEXT_ATTRIBUTE"; + + /** + * 获取 Mono < ServerRequest > + * + * @return / + */ + public static Mono getRequest() { + return Mono.deferContextual(Mono::just).map(e -> e.get(REQUEST_CONTEXT_ATTRIBUTE)); + } + + /** + * 获取 Mono < ServerResponse > + * + * @return / + */ + public static Mono getResponse() { + return Mono.deferContextual(Mono::just).map(e -> e.get(RESPONSE_CONTEXT_ATTRIBUTE)); + } + + /** + * 将 ServerRequest/ServerResponse 写入到同步上下文中,并执行一段代码,执行完毕清除上下文 + * + * @return / + */ + public static Mono sync(SaRetGenericFunction fun) { + return Mono.deferContextual(ctx -> { + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(ctx.get(REQUEST_CONTEXT_ATTRIBUTE), ctx.get(RESPONSE_CONTEXT_ATTRIBUTE)); + try { + return Mono.just(fun.run()); + } finally { + SaTokenContextUtil.clearContext(prev); + } + }); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/path/ApplicationContextPathLoading.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/path/ApplicationContextPathLoading.java new file mode 100644 index 00000000..7d282448 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/context/path/ApplicationContextPathLoading.java @@ -0,0 +1,52 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.context.path; + +import cn.dev33.satoken.application.ApplicationInfo; +import cn.dev33.satoken.util.SaFoxUtil; +import com.kfyty.loveqq.framework.core.autoconfig.CommandLineRunner; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Value; + +/** + * 应用上下文路径加载器 + * + * @author click33 + * @since 1.37.0 + */ +public class ApplicationContextPathLoading implements CommandLineRunner { + @Value("${k.mvc.tomcat.contextPath:}") + private String contextPath; + + @Override + public void run(String... args) throws Exception { + + String routePrefix = ""; + + if (SaFoxUtil.isNotEmpty(contextPath)) { + if (!contextPath.startsWith("/")) { + contextPath = "/" + contextPath; + } + if (contextPath.endsWith("/")) { + contextPath = contextPath.substring(0, contextPath.length() - 1); + } + routePrefix += contextPath; + } + + if (SaFoxUtil.isNotEmpty(routePrefix) && !routePrefix.equals("/")) { + ApplicationInfo.routePrefix = routePrefix; + } + } +} \ No newline at end of file diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java new file mode 100644 index 00000000..2669f03e --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java @@ -0,0 +1,72 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.filter; + +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.exception.BackResultException; +import cn.dev33.satoken.exception.FirewallCheckException; +import cn.dev33.satoken.exception.StopMatchException; +import cn.dev33.satoken.loveqq.boot.model.LoveqqSaRequest; +import cn.dev33.satoken.loveqq.boot.model.LoveqqSaResponse; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenOperateUtil; +import cn.dev33.satoken.strategy.SaFirewallStrategy; +import cn.dev33.satoken.util.SaTokenConsts; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Order; +import com.kfyty.loveqq.framework.web.core.filter.Filter; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +/** + * 防火墙校验过滤器 + * + * @author click33 + * @since 1.37.0 + */ +@Component +@Order(SaTokenConsts.FIREWALL_CHECK_FILTER_ORDER) +public class SaFirewallCheckFilter implements Filter { + + @Override + public Continue doFilter(ServerRequest request, ServerResponse response) { + LoveqqSaRequest saRequest = new LoveqqSaRequest(request); + LoveqqSaResponse saResponse = new LoveqqSaResponse(response); + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response); + try { + SaFirewallStrategy.instance.check.execute(saRequest, saResponse, null); + } catch (StopMatchException ignored) { + // ignored + } catch (BackResultException e) { + SaTokenOperateUtil.writeResult(response, e.getMessage()); + return Continue.FALSE; + } catch (FirewallCheckException e) { + if (SaFirewallStrategy.instance.checkFailHandle == null) { + SaTokenOperateUtil.writeResult(response, e.getMessage()); + } else { + SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null); + } + return Continue.FALSE; + } finally { + SaTokenContextUtil.clearContext(prev); + } + + // 更多异常则不处理,交由 Web 框架处理 + + // 向内执行 + return Continue.TRUE; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java new file mode 100644 index 00000000..b0a44904 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java @@ -0,0 +1,151 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.filter; + +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.exception.BackResultException; +import cn.dev33.satoken.exception.SaTokenException; +import cn.dev33.satoken.exception.StopMatchException; +import cn.dev33.satoken.filter.SaFilter; +import cn.dev33.satoken.filter.SaFilterAuthStrategy; +import cn.dev33.satoken.filter.SaFilterErrorStrategy; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenOperateUtil; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.util.SaTokenConsts; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Order; +import com.kfyty.loveqq.framework.web.core.filter.Filter; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 全局鉴权过滤器 + *

+ * 默认优先级为 -100,尽量保证在其它过滤器之前执行 + *

+ * + * @author click33 + * @since 1.19.0 + */ +@Order(SaTokenConsts.ASSEMBLY_ORDER) +public class SaRequestFilter implements SaFilter, Filter { + + // ------------------------ 设置此过滤器 拦截 & 放行 的路由 + + /** + * 拦截路由 + */ + public List includeList = new ArrayList<>(); + + /** + * 放行路由 + */ + public List excludeList = new ArrayList<>(); + + @Override + public SaRequestFilter addInclude(String... paths) { + includeList.addAll(Arrays.asList(paths)); + return this; + } + + @Override + public SaRequestFilter addExclude(String... paths) { + excludeList.addAll(Arrays.asList(paths)); + return this; + } + + @Override + public SaRequestFilter setIncludeList(List pathList) { + includeList = pathList; + return this; + } + + @Override + public SaRequestFilter setExcludeList(List pathList) { + excludeList = pathList; + return this; + } + + + // ------------------------ 钩子函数 + + /** + * 认证函数:每次请求执行 + */ + public SaFilterAuthStrategy auth = r -> { + }; + + /** + * 异常处理函数:每次[认证函数]发生异常时执行此函数 + */ + public SaFilterErrorStrategy error = e -> { + throw new SaTokenException(e); + }; + + /** + * 前置函数:在每次[认证函数]之前执行 + * 注意点:前置认证函数将不受 includeList 与 excludeList 的限制,所有路由的请求都会进入 beforeAuth + */ + public SaFilterAuthStrategy beforeAuth = r -> { + }; + + @Override + public SaRequestFilter setAuth(SaFilterAuthStrategy auth) { + this.auth = auth; + return this; + } + + @Override + public SaRequestFilter setError(SaFilterErrorStrategy error) { + this.error = error; + return this; + } + + @Override + public SaRequestFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) { + this.beforeAuth = beforeAuth; + return this; + } + + + // ------------------------ doFilter + + @Override + public Continue doFilter(ServerRequest request, ServerResponse response) { + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response); + try { + beforeAuth.run(null); + SaRouter.match(includeList).notMatch(excludeList).check(r -> auth.run(null)); + } catch (StopMatchException ignored) { + // ignored + } catch (BackResultException e) { + SaTokenOperateUtil.writeResult(response, e.getMessage()); + return Continue.FALSE; + } catch (Throwable e) { + SaTokenOperateUtil.writeResult(response, String.valueOf(error.run(e))); + return Continue.FALSE; + } finally { + SaTokenContextUtil.clearContext(prev); + } + + // 执行 + return Continue.TRUE; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java new file mode 100644 index 00000000..fbd2d297 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java @@ -0,0 +1,42 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.filter; + +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Order; +import com.kfyty.loveqq.framework.web.core.filter.Filter; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +/** + * SaTokenContext 上下文初始化过滤器 (基于 Servlet) + * + * @author click33 + * @since 1.42.0 + */ +@Component +@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER) +public class SaTokenContextFilter implements Filter { + + @Override + public Continue doFilter(ServerRequest request, ServerResponse response) { + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response); + return Continue.ofTrue(() -> SaTokenContextUtil.clearContext(prev)); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java new file mode 100644 index 00000000..2fdeecb4 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java @@ -0,0 +1,58 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.filter; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.exception.BackResultException; +import cn.dev33.satoken.exception.StopMatchException; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenOperateUtil; +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.util.SaTokenConsts; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Order; +import com.kfyty.loveqq.framework.web.core.filter.Filter; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +/** + * CORS 跨域策略过滤器 + * + * @author click33 + * @since 1.42.0 + */ +@Component +@Order(SaTokenConsts.CORS_FILTER_ORDER) +public class SaTokenCorsFilter implements Filter { + + @Override + public Continue doFilter(ServerRequest request, ServerResponse response) { + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response); + try { + SaTokenContextModelBox box = SaHolder.getContext().getModelBox(); + SaStrategy.instance.corsHandle.execute(box.getRequest(), box.getResponse(), box.getStorage()); + } catch (StopMatchException ignored) { + // ignored + } catch (BackResultException e) { + SaTokenOperateUtil.writeResult(response, e.getMessage()); + return Continue.FALSE; + } finally { + SaTokenContextUtil.clearContext(prev); + } + return Continue.TRUE; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/interceptor/SaInterceptor.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/interceptor/SaInterceptor.java new file mode 100644 index 00000000..390840fd --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/interceptor/SaInterceptor.java @@ -0,0 +1,139 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.interceptor; + +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.exception.BackResultException; +import cn.dev33.satoken.exception.StopMatchException; +import cn.dev33.satoken.fun.SaParamFunction; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenContextUtil; +import cn.dev33.satoken.loveqq.boot.utils.SaTokenOperateUtil; +import cn.dev33.satoken.strategy.SaAnnotationStrategy; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; +import com.kfyty.loveqq.framework.web.core.interceptor.HandlerInterceptor; +import com.kfyty.loveqq.framework.web.core.mapping.MethodMapping; + +import java.lang.reflect.Method; + +/** + * Sa-Token 综合拦截器,提供注解鉴权和路由拦截鉴权能力 + * + * @author click33 + * @since 1.31.0 + */ +public class SaInterceptor implements HandlerInterceptor { + /** + * 是否打开注解鉴权,配置为 true 时注解鉴权才会生效,配置为 false 时,即使写了注解也不会进行鉴权 + */ + public boolean isAnnotation = true; + + /** + * 认证前置函数:在注解鉴权之前执行 + *

参数:路由处理函数指针 + */ + public SaParamFunction beforeAuth = handler -> { + }; + + /** + * 认证函数:每次请求执行 + *

参数:路由处理函数指针 + */ + public SaParamFunction auth = handler -> { + }; + + /** + * 创建一个 Sa-Token 综合拦截器,默认带有注解鉴权能力 + */ + public SaInterceptor() { + } + + /** + * 创建一个 Sa-Token 综合拦截器,默认带有注解鉴权能力 + * + * @param auth 认证函数,每次请求执行 + */ + public SaInterceptor(SaParamFunction auth) { + this.auth = auth; + } + + /** + * 设置是否打开注解鉴权:配置为 true 时注解鉴权才会生效,配置为 false 时,即使写了注解也不会进行鉴权 + * + * @param isAnnotation / + * @return 对象自身 + */ + public SaInterceptor isAnnotation(boolean isAnnotation) { + this.isAnnotation = isAnnotation; + return this; + } + + /** + * 写入 [ 认证前置函数 ]: 在注解鉴权之前执行 + * + * @param beforeAuth / + * @return 对象自身 + */ + public SaInterceptor setBeforeAuth(SaParamFunction beforeAuth) { + this.beforeAuth = beforeAuth; + return this; + } + + /** + * 写入 [ 认证函数 ]: 每次请求执行 + * + * @param auth / + * @return 对象自身 + */ + public SaInterceptor setAuth(SaParamFunction auth) { + this.auth = auth; + return this; + } + + + // ----------------- 验证方法 ----------------- + + /** + * 每次请求之前触发的方法 + */ + @Override + public boolean preHandle(ServerRequest request, ServerResponse response, MethodMapping handler) { + SaTokenContextModelBox prev = SaTokenContextUtil.setContext(request, response); + try { + // 前置函数:在注解鉴权之前执行 + beforeAuth.run(handler); + + // 这里必须确保 handler 是 HandlerMethod 类型时,才能进行注解鉴权 + if (isAnnotation) { + Method method = handler.getMappingMethod(); + SaAnnotationStrategy.instance.checkMethodAnnotation.accept(method); + } + + // Auth 路由拦截鉴权校验 + auth.run(handler); + } catch (StopMatchException e) { + // StopMatchException 异常代表:停止匹配,进入Controller + } catch (BackResultException e) { + SaTokenOperateUtil.writeResult(response, e.getMessage()); + return false; + } finally { + SaTokenContextUtil.clearContext(prev); + } + + // 通过验证 + return true; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaRequest.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaRequest.java new file mode 100644 index 00000000..431f517b --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaRequest.java @@ -0,0 +1,116 @@ +package cn.dev33.satoken.loveqq.boot.model; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.application.ApplicationInfo; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.util.SaFoxUtil; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +import java.net.HttpCookie; +import java.util.Collection; +import java.util.Map; + +/** + * 对 SaRequest 包装类的实现 + * + * @author kfyty725 + */ +public class LoveqqSaRequest implements SaRequest { + /** + * loveqq-framework 包装请求 + */ + private final ServerRequest request; + + public LoveqqSaRequest(ServerRequest request) { + this.request = request; + } + + @Override + public Object getSource() { + return request; + } + + @Override + public String getParam(String name) { + return request.getParameter(name); + } + + @Override + public Collection getParamNames() { + return request.getParameterNames(); + } + + @Override + public Map getParamMap() { + return request.getParameterMap(); + } + + @Override + public String getHeader(String name) { + return request.getHeader(name); + } + + @Override + public String getCookieValue(String name) { + HttpCookie cookie = request.getCookie(name); + return cookie == null ? null : cookie.getValue(); + } + + @Override + public String getCookieFirstValue(String name) { + HttpCookie[] cookies = request.getCookies(); + if (cookies != null) { + for (HttpCookie cookie : cookies) { + if (cookie != null && name.equals(cookie.getName())) { + return cookie.getValue(); + } + } + } + return null; + } + + @Override + public String getCookieLastValue(String name) { + String value = null; + HttpCookie[] cookies = request.getCookies(); + if (cookies != null) { + for (HttpCookie cookie : cookies) { + if (cookie != null && name.equals(cookie.getName())) { + value = cookie.getValue(); + } + } + } + return value; + } + + @Override + public String getRequestPath() { + return ApplicationInfo.cutPathPrefix(request.getRequestURI()); + } + + @Override + public String getUrl() { + String currDomain = SaManager.getConfig().getCurrDomain(); + if (!SaFoxUtil.isEmpty(currDomain)) { + return currDomain + this.getRequestPath(); + } + return request.getRequestURL(); + } + + @Override + public String getMethod() { + return request.getMethod(); + } + + @Override + public String getHost() { + return request.getHost(); + } + + @Override + public Object forward(String path) { + ServerResponse response = (ServerResponse) SaManager.getSaTokenContext().getResponse().getSource(); + return response.sendRedirect(path); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaResponse.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaResponse.java new file mode 100644 index 00000000..af5c7d13 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaResponse.java @@ -0,0 +1,48 @@ +package cn.dev33.satoken.loveqq.boot.model; + +import cn.dev33.satoken.context.model.SaResponse; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +/** + * 对 SaResponse 包装类的实现 + * + * @author kfyty725 + */ +public class LoveqqSaResponse implements SaResponse { + /** + * loveqq-framework 包装响应 + */ + private final ServerResponse response; + + public LoveqqSaResponse(ServerResponse response) { + this.response = response; + } + + @Override + public Object getSource() { + return response; + } + + @Override + public SaResponse setStatus(int sc) { + response.setStatus(sc); + return this; + } + + @Override + public SaResponse setHeader(String name, String value) { + response.setHeader(name, value); + return this; + } + + @Override + public SaResponse addHeader(String name, String value) { + response.addHeader(name, value); + return this; + } + + @Override + public Object redirect(String url) { + return response.sendRedirect(url); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaStorage.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaStorage.java new file mode 100644 index 00000000..835ace09 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/model/LoveqqSaStorage.java @@ -0,0 +1,42 @@ +package cn.dev33.satoken.loveqq.boot.model; + +import cn.dev33.satoken.context.model.SaStorage; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; + +/** + * 对 SaStorage 包装类的实现 + * + * @author kfyty725 + */ +public class LoveqqSaStorage implements SaStorage { + /** + * loveqq-framework 包装请求 + */ + private final ServerRequest request; + + public LoveqqSaStorage(ServerRequest request) { + this.request = request; + } + + @Override + public Object getSource() { + return request; + } + + @Override + public Object get(String key) { + return request.getAttribute(key); + } + + @Override + public SaStorage set(String key, Object value) { + request.setAttribute(key, value); + return this; + } + + @Override + public SaStorage delete(String key) { + request.removeAttribute(key); + return this; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanInject.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanInject.java new file mode 100644 index 00000000..d47f6c29 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanInject.java @@ -0,0 +1,153 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.oauth2; + +import cn.dev33.satoken.oauth2.SaOAuth2Manager; +import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig; +import cn.dev33.satoken.oauth2.dao.SaOAuth2Dao; +import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter; +import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate; +import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader; +import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver; +import cn.dev33.satoken.oauth2.granttype.handler.SaOAuth2GrantTypeHandlerInterface; +import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor; +import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface; +import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; +import cn.dev33.satoken.oauth2.template.SaOAuth2Template; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +import java.util.List; + + +// 小提示:如果你在 idea 中运行源码时出现异常:java: 程序包cn.dev33.satoken.oauth2不存在。 +// 在项目根目录进入 cmd,执行 mvn package 即可解决 + + +/** + * 注入 Sa-Token-OAuth2 所需要的组件 + * + * @author click33 + * @since 1.34.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.oauth2.SaOAuth2Manager") +public class SaOAuth2BeanInject { + /** + * 注入 OAuth2 配置对象 + * + * @param saOAuth2Config 配置对象 + */ + @Autowired(required = false) + public void setSaOAuth2Config(SaOAuth2ServerConfig saOAuth2Config) { + SaOAuth2Manager.setServerConfig(saOAuth2Config); + } + + /** + * 注入 OAuth2 模板代码类 + * + * @param saOAuth2Template 模板代码类 + */ + @Autowired(required = false) + public void setSaOAuth2Template(SaOAuth2Template saOAuth2Template) { + SaOAuth2Manager.setTemplate(saOAuth2Template); + } + + /** + * 注入 OAuth2 请求处理器 + * + * @param serverProcessor 请求处理器 + */ + @Autowired(required = false) + public void setSaOAuth2Template(SaOAuth2ServerProcessor serverProcessor) { + SaOAuth2ServerProcessor.instance = serverProcessor; + } + + /** + * 注入 OAuth2 数据加载器 + * + * @param dataLoader / + */ + @Autowired(required = false) + public void setSaOAuth2DataLoader(SaOAuth2DataLoader dataLoader) { + SaOAuth2Manager.setDataLoader(dataLoader); + } + + /** + * 注入 OAuth2 数据解析器 Bean + * + * @param dataResolver / + */ + @Autowired(required = false) + public void setSaOAuth2DataResolver(SaOAuth2DataResolver dataResolver) { + SaOAuth2Manager.setDataResolver(dataResolver); + } + + /** + * 注入 OAuth2 数据格式转换器 Bean + * + * @param dataConverter / + */ + @Autowired(required = false) + public void setSaOAuth2DataConverter(SaOAuth2DataConverter dataConverter) { + SaOAuth2Manager.setDataConverter(dataConverter); + } + + /** + * 注入 OAuth2 数据构建器 Bean + * + * @param dataGenerate / + */ + @Autowired(required = false) + public void setSaOAuth2DataGenerate(SaOAuth2DataGenerate dataGenerate) { + SaOAuth2Manager.setDataGenerate(dataGenerate); + } + + /** + * 注入 OAuth2 数据持久 Bean + * + * @param dao / + */ + @Autowired(required = false) + public void setSaOAuth2Dao(SaOAuth2Dao dao) { + SaOAuth2Manager.setDao(dao); + } + + /** + * 注入自定义 scope 处理器 + * + * @param handlerList 自定义 scope 处理器集合 + */ + @Autowired(required = false) + public void setSaOAuth2ScopeHandler(List handlerList) { + for (SaOAuth2ScopeHandlerInterface handler : handlerList) { + SaOAuth2Strategy.instance.registerScopeHandler(handler); + } + } + + /** + * 注入自定义 grant_type 处理器 + * + * @param handlerList 自定义 grant_type 处理器集合 + */ + @Autowired(required = false) + public void setSaOAuth2GrantTypeHandlerInterface(List handlerList) { + for (SaOAuth2GrantTypeHandlerInterface handler : handlerList) { + SaOAuth2Strategy.instance.registerGrantTypeHandler(handler); + } + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanRegister.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanRegister.java new file mode 100644 index 00000000..15b7a955 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/oauth2/SaOAuth2BeanRegister.java @@ -0,0 +1,43 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.oauth2; + +import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.ConfigurationProperties; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注册 Sa-Token-OAuth2 所需要的Bean + * + * @author click33 + * @since 1.34.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.oauth2.SaOAuth2Manager") +public class SaOAuth2BeanRegister { + /** + * 获取 OAuth2 配置 Bean + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token.oauth2-server") + public SaOAuth2ServerConfig getSaOAuth2Config() { + return new SaOAuth2ServerConfig(); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/package-info.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/package-info.java new file mode 100644 index 00000000..34dce2ca --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/package-info.java @@ -0,0 +1,19 @@ +/* + * 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. + */ +/** + * Sa-Token 集成 loveqq-framework 的各个组件 + */ +package cn.dev33.satoken.loveqq.boot; \ No newline at end of file diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanInject.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanInject.java new file mode 100644 index 00000000..a7d75168 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanInject.java @@ -0,0 +1,64 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.sign; + +import cn.dev33.satoken.sign.SaSignManager; +import cn.dev33.satoken.sign.config.SaSignConfig; +import cn.dev33.satoken.sign.config.SaSignManyConfigWrapper; +import cn.dev33.satoken.sign.template.SaSignTemplate; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注入 Sa-Token API 参数签名 所需要的 Bean + * + * @author click33 + * @since 1.43.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.sign.SaSignManager") +public class SaSignBeanInject { + /** + * 注入 API 参数签名配置对象 + * + * @param saSignConfig 配置对象 + */ + @Autowired(required = false) + public void setSignConfig(SaSignConfig saSignConfig) { + SaSignManager.setConfig(saSignConfig); + } + + /** + * 注入 API 参数签名配置对象 + * + * @param signManyConfigWrapper 配置对象 + */ + @Autowired(required = false) + public void setSignManyConfig(SaSignManyConfigWrapper signManyConfigWrapper) { + SaSignManager.setSignMany(signManyConfigWrapper.getSignMany()); + } + + /** + * 注入自定义的 参数签名 模版方法 Bean + * + * @param saSignTemplate 参数签名 Bean + */ + @Autowired(required = false) + public void setSaSignTemplate(SaSignTemplate saSignTemplate) { + SaSignManager.setSaSignTemplate(saSignTemplate); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanRegister.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanRegister.java new file mode 100644 index 00000000..4d39dd25 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sign/SaSignBeanRegister.java @@ -0,0 +1,55 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.sign; + +import cn.dev33.satoken.sign.config.SaSignConfig; +import cn.dev33.satoken.sign.config.SaSignManyConfigWrapper; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.ConfigurationProperties; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注册 Sa-Token API 参数签名所需要的 Bean + * + * @author click33 + * @since 1.43.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.sign.SaSignManager") +public class SaSignBeanRegister { + /** + * 获取 API 参数签名配置对象 + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token.sign") + public SaSignConfig getSaSignConfig() { + return new SaSignConfig(); + } + + /** + * 获取 API 参数签名 Many 配置对象 + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token") + public SaSignManyConfigWrapper getSaSignManyConfigWrapper() { + return new SaSignManyConfigWrapper(); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanInject.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanInject.java new file mode 100644 index 00000000..c85bcd35 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanInject.java @@ -0,0 +1,77 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.sso; + +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Autowired; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; + +/** + * 注入 Sa-Token SSO 所需要的 Bean + * + * @author click33 + * @since 1.34.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.sso.SaSsoManager") +public class SaSsoBeanInject { + /** + * 注入 Sa-Token SSO Server 端 配置类 + * + * @param serverConfig 配置对象 + */ + @Autowired(required = false) + public void setSaSsoServerConfig(SaSsoServerConfig serverConfig) { + SaSsoManager.setServerConfig(serverConfig); + } + + /** + * 注入 Sa-Token SSO Client 端 配置类 + * + * @param clientConfig 配置对象 + */ + @Autowired(required = false) + public void setSaSsoClientConfig(SaSsoClientConfig clientConfig) { + SaSsoManager.setClientConfig(clientConfig); + } + + /** + * 注入 SSO 模板代码类 (Server 端) + * + * @param ssoServerTemplate / + */ + @Autowired(required = false) + public void setSaSsoServerTemplate(SaSsoServerTemplate ssoServerTemplate) { + SaSsoServerProcessor.instance.ssoServerTemplate = ssoServerTemplate; + } + + /** + * 注入 SSO 模板代码类 (Client 端) + * + * @param ssoClientTemplate / + */ + @Autowired(required = false) + public void setSaSsoClientTemplate(SaSsoClientTemplate ssoClientTemplate) { + SaSsoClientProcessor.instance.ssoClientTemplate = ssoClientTemplate; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanRegister.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanRegister.java new file mode 100644 index 00000000..b7cf272a --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/sso/SaSsoBeanRegister.java @@ -0,0 +1,82 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.sso; + +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Bean; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.Component; +import com.kfyty.loveqq.framework.core.autoconfig.annotation.ConfigurationProperties; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnClass; +import com.kfyty.loveqq.framework.core.autoconfig.condition.annotation.ConditionalOnMissingBean; + +/** + * 注册 Sa-Token SSO 所需要的 Bean + * + * @author click33 + * @since 1.34.0 + */ +@Component +@ConditionalOnClass("cn.dev33.satoken.sso.SaSsoManager") +public class SaSsoBeanRegister { + /** + * 获取 SSO Server 端 配置对象 + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token.sso-server") + public SaSsoServerConfig getSaSsoServerConfig() { + return new SaSsoServerConfig(); + } + + /** + * 获取 SSO Client 端 配置对象 + * + * @return 配置对象 + */ + @Bean + @ConfigurationProperties("sa-token.sso-client") + public SaSsoClientConfig getSaSsoClientConfig() { + return new SaSsoClientConfig(); + } + + /** + * 获取 SSO Server 端 SaSsoServerTemplate + * + * @return / + */ + @Bean + @ConditionalOnMissingBean(SaSsoServerTemplate.class) + public SaSsoServerTemplate getSaSsoServerTemplate() { + return SaSsoServerProcessor.instance.ssoServerTemplate; + } + + /** + * 获取 SSO Client 端 SaSsoClientTemplate + * + * @return / + */ + @Bean + @ConditionalOnMissingBean(SaSsoClientTemplate.class) + public SaSsoClientTemplate getSaSsoClientTemplate() { + return SaSsoClientProcessor.instance.ssoClientTemplate; + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/support/SaPathMatcherHolder.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/support/SaPathMatcherHolder.java new file mode 100644 index 00000000..bb4556f0 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/support/SaPathMatcherHolder.java @@ -0,0 +1,68 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.support; + +import com.kfyty.loveqq.framework.core.support.AntPathMatcher; +import com.kfyty.loveqq.framework.core.support.PatternMatcher; + +/** + * 路由匹配工具类:持有 PathMatcher 全局引用,方便快捷的调用 PathMatcher 相关方法 + * + * @author click33 + * @since 1.34.0 + */ +public class SaPathMatcherHolder { + + private SaPathMatcherHolder() { + } + + /** + * 路由匹配器 + */ + public static PatternMatcher pathMatcher; + + /** + * 获取路由匹配器 + * + * @return 路由匹配器 + */ + public static PatternMatcher getPathMatcher() { + if (pathMatcher == null) { + pathMatcher = new AntPathMatcher(); + } + return pathMatcher; + } + + /** + * 写入路由匹配器 + * + * @param pathMatcher 路由匹配器 + */ + public static void setPathMatcher(PatternMatcher pathMatcher) { + SaPathMatcherHolder.pathMatcher = pathMatcher; + } + + /** + * 判断:指定路由匹配符是否可以匹配成功指定路径 + * + * @param pattern 路由匹配符 + * @param path 要匹配的路径 + * @return 是否匹配成功 + */ + public static boolean match(String pattern, String path) { + return getPathMatcher().matches(pattern, path); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java new file mode 100644 index 00000000..3d1bea75 --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java @@ -0,0 +1,125 @@ +/* + * 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 cn.dev33.satoken.loveqq.boot.utils; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStaff; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.context.model.SaStorage; +import cn.dev33.satoken.context.model.SaTokenContextModelBox; +import cn.dev33.satoken.fun.SaFunction; +import cn.dev33.satoken.fun.SaRetGenericFunction; +import cn.dev33.satoken.loveqq.boot.model.LoveqqSaRequest; +import cn.dev33.satoken.loveqq.boot.model.LoveqqSaResponse; +import cn.dev33.satoken.loveqq.boot.model.LoveqqSaStorage; +import com.kfyty.loveqq.framework.web.core.http.ServerRequest; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +/** + * SaTokenContext 上下文读写工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaTokenContextUtil { + /** + * 写入当前上下文 + * + * @param request / + * @param response / + */ + public static SaTokenContextModelBox setContext(ServerRequest request, ServerResponse response) { + SaTokenContextModelBox prev = SaTokenContextForThreadLocalStaff.getModelBoxOrNull(); + SaRequest req = new LoveqqSaRequest(request); + SaResponse res = new LoveqqSaResponse(response); + SaStorage stg = new LoveqqSaStorage(request); + SaManager.getSaTokenContext().setContext(req, res, stg); + return prev; + } + + /** + * 写入上下文对象, 并在执行函数后将其清除 + * + * @param request / + * @param response / + * @param fun / + */ + public static void setContext(ServerRequest request, ServerResponse response, SaFunction fun) { + SaTokenContextModelBox prev = setContext(request, response); + try { + fun.run(); + } finally { + clearContext(prev); + } + } + + /** + * 写入上下文对象, 并在执行函数后将其清除 + * + * @param request / + * @param response / + * @param fun / + * @param / + * @return / + */ + public static T setContext(ServerRequest request, ServerResponse response, SaRetGenericFunction fun) { + SaTokenContextModelBox prev = setContext(request, response); + try { + return fun.run(); + } finally { + clearContext(prev); + } + } + + /** + * 清除当前上下文 + */ + public static void clearContext(SaTokenContextModelBox prev) { + if (prev == null) { + SaManager.getSaTokenContext().clearContext(); + } else { + SaManager.getSaTokenContext().setContext(prev.getRequest(), prev.getResponse(), prev.getStorage()); + } + } + + /** + * 获取当前 ModelBox + * + * @return / + */ + public static SaTokenContextModelBox getModelBox() { + return SaManager.getSaTokenContext().getModelBox(); + } + + /** + * 获取当前 Request + * + * @return / + */ + public static ServerRequest getRequest() { + return (ServerRequest) getModelBox().getRequest().getSource(); + } + + /** + * 获取当前 Response + * + * @return / + */ + public static ServerResponse getResponse() { + return (ServerResponse) getModelBox().getResponse().getSource(); + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenOperateUtil.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenOperateUtil.java new file mode 100644 index 00000000..c7be3d6c --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenOperateUtil.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 cn.dev33.satoken.loveqq.boot.utils; + +import cn.dev33.satoken.util.SaTokenConsts; +import com.kfyty.loveqq.framework.core.exception.ResolvableException; +import com.kfyty.loveqq.framework.web.core.http.ServerResponse; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +/** + * {@link ServerResponse} 操作工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaTokenOperateUtil { + /** + * 写入结果到输出流 + * + * @param response / + * @param result / + */ + public static void writeResult(ServerResponse response, String result) { + // 写入输出流 + // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json + // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); + if (response.getContentType() == null) { + response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); + } + try (OutputStream out = response.getOutputStream()) { + out.write(result.getBytes(StandardCharsets.UTF_8)); + out.flush(); + } catch (IOException e) { + throw new ResolvableException(e); + } + } +} diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/resources/META-INF/k.factories b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/resources/META-INF/k.factories new file mode 100644 index 00000000..c7370f8c --- /dev/null +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/resources/META-INF/k.factories @@ -0,0 +1,11 @@ +com.kfyty.loveqq.framework.core.autoconfig.annotation.EnableAutoConfiguration=\ + cn.dev33.satoken.loveqq.boot.SaBeanRegister,\ + cn.dev33.satoken.loveqq.boot.SaBeanInject,\ + cn.dev33.satoken.loveqq.boot.apiKey.SaApiKeyBeanRegister,\ + cn.dev33.satoken.loveqq.boot.apiKey.SaApiKeyBeanInject,\ + cn.dev33.satoken.loveqq.boot.oauth2.SaOAuth2BeanRegister,\ + cn.dev33.satoken.loveqq.boot.oauth2.SaOAuth2BeanInject,\ + cn.dev33.satoken.loveqq.boot.sign.SaSignBeanRegister,\ + cn.dev33.satoken.loveqq.boot.sign.SaSignBeanInject,\ + cn.dev33.satoken.loveqq.boot.sso.SaSsoBeanRegister,\ + cn.dev33.satoken.loveqq.boot.sso.SaSsoBeanInject From be5ee2fc2ba8e65755827d75ae20766d913a5fb2 Mon Sep 17 00:00:00 2001 From: kfyty725 Date: Wed, 4 Jun 2025 16:12:23 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:loveqq-framework=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../satoken/loveqq/boot/filter/SaFirewallCheckFilter.java | 2 +- .../cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java | 2 +- .../satoken/loveqq/boot/filter/SaTokenContextFilter.java | 2 +- .../dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java | 4 +++- .../dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java | 2 ++ 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java index 2669f03e..304ce3e9 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaFirewallCheckFilter.java @@ -32,7 +32,7 @@ import com.kfyty.loveqq.framework.web.core.http.ServerRequest; import com.kfyty.loveqq.framework.web.core.http.ServerResponse; /** - * 防火墙校验过滤器 + * 防火墙校验过滤器 (基于 loveqq-framework 统一 Filter,可以统一 servlet 和 reactor 配置) * * @author click33 * @since 1.37.0 diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java index b0a44904..a05a2268 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaRequestFilter.java @@ -36,7 +36,7 @@ import java.util.Arrays; import java.util.List; /** - * 全局鉴权过滤器 + * 全局鉴权过滤器 (基于 loveqq-framework 统一 Filter,可以统一 servlet 和 reactor 配置) *

* 默认优先级为 -100,尽量保证在其它过滤器之前执行 *

diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java index fbd2d297..ba38d368 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenContextFilter.java @@ -25,7 +25,7 @@ import com.kfyty.loveqq.framework.web.core.http.ServerRequest; import com.kfyty.loveqq.framework.web.core.http.ServerResponse; /** - * SaTokenContext 上下文初始化过滤器 (基于 Servlet) + * SaTokenContext 上下文初始化过滤器 (基于 loveqq-framework 统一 Filter,可以统一 servlet 和 reactor 配置) * * @author click33 * @since 1.42.0 diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java index 2fdeecb4..9dac1807 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/filter/SaTokenCorsFilter.java @@ -30,9 +30,11 @@ import com.kfyty.loveqq.framework.web.core.http.ServerRequest; import com.kfyty.loveqq.framework.web.core.http.ServerResponse; /** - * CORS 跨域策略过滤器 + * CORS 跨域策略过滤器 (基于 loveqq-framework 统一 Filter,可以统一 servlet 和 reactor 配置) + * loveqq-framework 也有跨域过滤器,切勿同时配置 * * @author click33 + * @see com.kfyty.loveqq.framework.web.core.cors.CorsFilter * @since 1.42.0 */ @Component diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java index 3d1bea75..911c6e58 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java +++ b/sa-token-starter/sa-token-loveqq-boot-starter/src/main/java/cn/dev33/satoken/loveqq/boot/utils/SaTokenContextUtil.java @@ -38,6 +38,7 @@ import com.kfyty.loveqq.framework.web.core.http.ServerResponse; public class SaTokenContextUtil { /** * 写入当前上下文 + * 并返回当前的上下文,以支持 loveqq-framework 的 servlet/reactor 的统一配置 * * @param request / * @param response / @@ -87,6 +88,7 @@ public class SaTokenContextUtil { /** * 清除当前上下文 + * 并恢复之前的上下文,以支持 loveqq-framework 的 servlet/reactor 的统一配置 */ public static void clearContext(SaTokenContextModelBox prev) { if (prev == null) { From bd22a94a10f76aeaf24874c7873003d9ccbcb96f Mon Sep 17 00:00:00 2001 From: kfyty725 Date: Wed, 4 Jun 2025 17:46:36 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat:=E9=9B=86=E6=88=90loveqq-framework?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-demo/pom.xml | 3 +- .../sa-token-demo-loveqq-boot/pom.xml | 74 +++++++++++ .../java/com/pj/SaTokenLoveqqApplication.java | 21 ++++ .../main/java/com/pj/satoken/MyFilter.java | 53 ++++++++ .../java/com/pj/satoken/SaTokenConfigure.java | 39 ++++++ .../java/com/pj/satoken/StpInterfaceImpl.java | 42 +++++++ .../java/com/pj/test/GlobalException.java | 18 +++ .../main/java/com/pj/test/TestController.java | 115 ++++++++++++++++++ .../main/java/com/pj/test/UserService.java | 24 ++++ .../src/main/resources/application.yml | 4 + 10 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/pom.xml create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/SaTokenLoveqqApplication.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/MyFilter.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/SaTokenConfigure.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/satoken/StpInterfaceImpl.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/GlobalException.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/TestController.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/java/com/pj/test/UserService.java create mode 100644 sa-token-demo/sa-token-demo-loveqq-boot/src/main/resources/application.yml 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 From e13b263f274f6290c58f854d59554f313148689e Mon Sep 17 00:00:00 2001 From: kfyty725 Date: Wed, 4 Jun 2025 17:48:49 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=E9=9B=86=E6=88=90loveqq-framework?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/pj/test/TestController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 48e5b4fc..cab37d89 100644 --- 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 @@ -17,7 +17,8 @@ import reactor.core.publisher.Mono; import java.time.Duration; /** - * 测试专用Controller + * 测试专用 Controller + * 本示例是基于 reactor 编写,如果是 servlet,去除 SaReactorHolder/SaTokenContextUtil 包装,直接调用 sa-token api 即可 * * @author click33 */ From 5fc40a5af2ea6179ac0df251907b38732816ebb8 Mon Sep 17 00:00:00 2001 From: kfyty725 Date: Wed, 4 Jun 2025 21:31:41 +0800 Subject: [PATCH 5/5] =?UTF-8?q?opt:loveqq-framework=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=99=A8=E6=94=AF=E6=8C=81java8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-dependencies/pom.xml | 2 +- sa-token-starter/sa-token-loveqq-boot-starter/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sa-token-dependencies/pom.xml b/sa-token-dependencies/pom.xml index 254a0fa3..5498e932 100644 --- a/sa-token-dependencies/pom.xml +++ b/sa-token-dependencies/pom.xml @@ -30,7 +30,7 @@ 3.2.133 4.9.17 3.14.4 - 1.1.2 + 1.1.2-java8 2.5.0 2.7.21 2.10.1.RELEASE diff --git a/sa-token-starter/sa-token-loveqq-boot-starter/pom.xml b/sa-token-starter/sa-token-loveqq-boot-starter/pom.xml index 77941acb..1c778110 100644 --- a/sa-token-starter/sa-token-loveqq-boot-starter/pom.xml +++ b/sa-token-starter/sa-token-loveqq-boot-starter/pom.xml @@ -16,7 +16,7 @@ loveqq-framework integrate sa-token - 17 + 1.8