This commit is contained in:
click33 2025-06-07 00:40:48 +08:00
commit c2fc4decef
38 changed files with 2493 additions and 1 deletions

View File

@ -57,6 +57,7 @@
<module>sa-token-demo-webflux-springboot3</module>
<module>sa-token-demo-websocket</module>
<module>sa-token-demo-websocket-spring</module>
<module>sa-token-demo-loveqq-boot</module>
</modules>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-framework</artifactId>
<version>1.1.2</version>
<relativePath/>
</parent>
<artifactId>sa-token-demo-loveqq-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<jdk.version>17</jdk.version>
<java.version>17</java.version>
<maven.source.version>17</maven.source.version>
<maven.compile.version>17</maven.compile.version>
<sa-token.version>1.43.0</sa-token.version>
</properties>
<dependencies>
<!-- 引导启动模块 -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-boot</artifactId>
<version>${loveqq.framework.version}</version>
</dependency>
<!-- reactor-netty 服务器,同时支持命令式/响应式编程范式 -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-boot-starter-netty</artifactId>
<version>${loveqq.framework.version}</version>
</dependency>
<!-- sa-token 集成 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-loveqq-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- logback 启动器 -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-boot-starter-logback</artifactId>
<version>${loveqq.framework.version}</version>
</dependency>
<!-- yaml 支持,默认使用 properties 文件,如果使用 yaml 需自行引入依赖 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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());
})
;
}
}

View File

@ -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<String> getPermissionList(Object loginId, String loginType) {
// 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
List<String> list = new ArrayList<String>();
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<String> getRoleList(Object loginId, String loginType) {
// 本list仅做模拟实际项目中要根据具体业务逻辑来查询角色
List<String> list = new ArrayList<String>();
list.add("admin");
list.add("super-admin");
return list;
}
}

View File

@ -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());
}
}

View File

@ -0,0 +1,116 @@
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
* 本示例是基于 reactor 编写如果是 servlet去除 SaReactorHolder/SaTokenContextUtil 包装直接调用 sa-token api 即可
*
* @author click33
*/
@RestController
@RequestMapping("/test/")
public class TestController {
@Autowired
UserService userService;
// 登录测试Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
@GetMapping("login")
public Mono<SaResult> 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<SaResult> 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<SaResult> 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<SaResult> 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();
}
}

View File

@ -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<Long> findUserIdByNamePwd(String name, String pwd) {
// ...
return Mono.just(10001L);
}
}

View File

@ -0,0 +1,4 @@
# 端口
k:
server:
port: 8081

View File

@ -30,6 +30,7 @@
<noear-snack3.version>3.2.133</noear-snack3.version>
<jfinal.version>4.9.17</jfinal.version>
<jboot.version>3.14.4</jboot.version>
<loveqq.version>1.1.2-java8</loveqq.version>
<commons-pool2.version>2.5.0</commons-pool2.version>
<dubbo.version>2.7.21</dubbo.version>
<grpc-spring-boot-starter.version>2.10.1.RELEASE</grpc-spring-boot-starter.version>
@ -125,6 +126,27 @@
<version>${jfinal.version}</version>
</dependency>
<!-- loveqq-core -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-core</artifactId>
<version>${loveqq.version}</version>
</dependency>
<!-- loveqq-mvc-core -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-mvc-core</artifactId>
<version>${loveqq.version}</version>
</dependency>
<!-- loveqq redisson starter -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-boot-starter-redisson</artifactId>
<version>${loveqq.version}</version>
</dependency>
<!-- test -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -28,6 +28,7 @@
<module>sa-token-solon-plugin</module>
<module>sa-token-jboot-plugin</module>
<module>sa-token-jfinal-plugin</module>
<module>sa-token-loveqq-boot-starter</module>
</modules>
</project>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-starter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>sa-token-loveqq-boot-starter</name>
<artifactId>sa-token-loveqq-boot-starter</artifactId>
<packaging>jar</packaging>
<description>loveqq-framework integrate sa-token</description>
<properties>
<jdk.version>1.8</jdk.version>
</properties>
<dependencies>
<!-- loveqq-core -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-core</artifactId>
</dependency>
<!-- loveqq-mvc-core -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-mvc-core</artifactId>
<scope>provided</scope>
</dependency>
<!-- jackson 序列化、redis集成、SSO、OAuth2 等模块要用到,比较重要所以内置集成 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
</dependency>
<!-- loveqq redisson starter -->
<dependency>
<groupId>com.kfyty</groupId>
<artifactId>loveqq-boot-starter-redisson</artifactId>
<scope>provided</scope>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<!-- SSO (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<optional>true</optional>
</dependency>
<!-- OAuth2.0 (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId>
<optional>true</optional>
</dependency>
<!-- API Key (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-apikey</artifactId>
<optional>true</optional>
</dependency>
<!-- API Sign (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -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 {
/**
* 组件注入
* <p> 为确保 Log 组件正常打印必须将 SaLog SaTokenConfig 率先初始化 </p>
*
* @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<SaTokenListener> listenerList) {
SaTokenEventCenter.registerListenerList(listenerList);
}
/**
* 注入自定义注解处理器
*
* @param handlerList 自定义注解处理器集合
*/
@Autowired(required = false)
public void setSaAnnotationHandler(List<SaAnnotationHandlerInterface<?>> 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<SaFirewallCheckHook> 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<SaTokenPlugin> plugins) {
for (SaTokenPlugin plugin : plugins) {
SaTokenPluginHolder.instance.installPlugin(plugin);
}
}
}

View File

@ -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
* <p> 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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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<ServerRequest> getRequest() {
return Mono.deferContextual(Mono::just).map(e -> e.get(REQUEST_CONTEXT_ATTRIBUTE));
}
/**
* 获取 Mono < ServerResponse >
*
* @return /
*/
public static Mono<ServerResponse> getResponse() {
return Mono.deferContextual(Mono::just).map(e -> e.get(RESPONSE_CONTEXT_ATTRIBUTE));
}
/**
* ServerRequest/ServerResponse 写入到同步上下文中并执行一段代码执行完毕清除上下文
*
* @return /
*/
public static <R> Mono<R> sync(SaRetGenericFunction<R> 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);
}
});
}
}

View File

@ -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;
}
}
}

View File

@ -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;
/**
* 防火墙校验过滤器 (基于 loveqq-framework 统一 Filter可以统一 servlet reactor 配置)
*
* @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;
}
}

View File

@ -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;
/**
* 全局鉴权过滤器 (基于 loveqq-framework 统一 Filter可以统一 servlet reactor 配置)
* <p>
* 默认优先级为 -100尽量保证在其它过滤器之前执行
* </p>
*
* @author click33
* @since 1.19.0
*/
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaRequestFilter implements SaFilter, Filter {
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
public List<String> includeList = new ArrayList<>();
/**
* 放行路由
*/
public List<String> 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<String> pathList) {
includeList = pathList;
return this;
}
@Override
public SaRequestFilter setExcludeList(List<String> pathList) {
excludeList = pathList;
return this;
}
// ------------------------ 钩子函数
/**
* 认证函数每次请求执行
*/
public SaFilterAuthStrategy auth = r -> {
};
/**
* 异常处理函数每次[认证函数]发生异常时执行此函数
*/
public SaFilterErrorStrategy error = e -> {
throw new SaTokenException(e);
};
/**
* 前置函数在每次[认证函数]之前执行
* <b>注意点前置认证函数将不受 includeList excludeList 的限制所有路由的请求都会进入 beforeAuth</b>
*/
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;
}
}

View File

@ -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 上下文初始化过滤器 (基于 loveqq-framework 统一 Filter可以统一 servlet reactor 配置)
*
* @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));
}
}

View File

@ -0,0 +1,60 @@
/*
* 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 跨域策略过滤器 (基于 loveqq-framework 统一 Filter可以统一 servlet reactor 配置)
* loveqq-framework 也有跨域过滤器切勿同时配置
*
* @author click33
* @see com.kfyty.loveqq.framework.web.core.cors.CorsFilter
* @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;
}
}

View File

@ -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;
/**
* 认证前置函数在注解鉴权之前执行
* <p> 参数路由处理函数指针
*/
public SaParamFunction<Object> beforeAuth = handler -> {
};
/**
* 认证函数每次请求执行
* <p> 参数路由处理函数指针
*/
public SaParamFunction<Object> auth = handler -> {
};
/**
* 创建一个 Sa-Token 综合拦截器默认带有注解鉴权能力
*/
public SaInterceptor() {
}
/**
* 创建一个 Sa-Token 综合拦截器默认带有注解鉴权能力
*
* @param auth 认证函数每次请求执行
*/
public SaInterceptor(SaParamFunction<Object> 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<Object> beforeAuth) {
this.beforeAuth = beforeAuth;
return this;
}
/**
* 写入 [ 认证函数 ]: 每次请求执行
*
* @param auth /
* @return 对象自身
*/
public SaInterceptor setAuth(SaParamFunction<Object> 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;
}
}

View File

@ -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<String> getParamNames() {
return request.getParameterNames();
}
@Override
public Map<String, String> 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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<SaOAuth2ScopeHandlerInterface> handlerList) {
for (SaOAuth2ScopeHandlerInterface handler : handlerList) {
SaOAuth2Strategy.instance.registerScopeHandler(handler);
}
}
/**
* 注入自定义 grant_type 处理器
*
* @param handlerList 自定义 grant_type 处理器集合
*/
@Autowired(required = false)
public void setSaOAuth2GrantTypeHandlerInterface(List<SaOAuth2GrantTypeHandlerInterface> handlerList) {
for (SaOAuth2GrantTypeHandlerInterface handler : handlerList) {
SaOAuth2Strategy.instance.registerGrantTypeHandler(handler);
}
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,127 @@
/*
* 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 {
/**
* 写入当前上下文
* 并返回当前的上下文以支持 loveqq-framework servlet/reactor 的统一配置
*
* @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 <T> /
* @return /
*/
public static <T> T setContext(ServerRequest request, ServerResponse response, SaRetGenericFunction<T> fun) {
SaTokenContextModelBox prev = setContext(request, response);
try {
return fun.run();
} finally {
clearContext(prev);
}
}
/**
* 清除当前上下文
* 并恢复之前的上下文以支持 loveqq-framework servlet/reactor 的统一配置
*/
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();
}
}

View File

@ -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);
}
}
}

View File

@ -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