filter(ServerWebExchange exchange, WebFilterChain chain) {
+
+ SaRequestForReactor saRequest = new SaRequestForReactor(exchange.getRequest());
+ SaResponseForReactor saResponse = new SaResponseForReactor(exchange.getResponse());
+
+ try {
+ SaReactorSyncHolder.setContext(exchange);
+ SaFirewallStrategy.instance.check.execute(saRequest, saResponse, exchange);
+ }
+ catch (StopMatchException ignored) {}
+ catch (BackResultException e) {
+ return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
+ }
+ // FirewallCheckException 异常则交由异常处理策略处理
+ catch (FirewallCheckException e) {
+ if(SaFirewallStrategy.instance.checkFailHandle == null) {
+ return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
+ } else {
+ SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null);
+ return Mono.empty();
+ }
+ }
+ finally {
+ SaReactorSyncHolder.clearContext();
+ }
+ // 更多异常则不处理,交由 Web 框架处理
+
+ // 向下执行
+ return chain.filter(exchange);
+ }
+
+}
\ No newline at end of file
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..92670f5f9e7b816cf6d9c6f42f0dfc6883e88930
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java
@@ -0,0 +1,148 @@
+/*
+ * 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.reactor.filter;
+
+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.reactor.context.SaReactorSyncHolder;
+import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.util.SaTokenConsts;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Reactor 全局鉴权过滤器
+ *
+ * 默认优先级为 -100,尽量保证在其它过滤器之前执行
+ *
+ *
+ * @author click33
+ * @since 1.34.0
+ */
+@Order(SaTokenConsts.ASSEMBLY_ORDER)
+public class SaReactorFilter implements SaFilter, GlobalFilter {
+
+ // ------------------------ 设置此过滤器 拦截 & 放行 的路由
+
+ /**
+ * 拦截路由
+ */
+ public List includeList = new ArrayList<>();
+
+ /**
+ * 放行路由
+ */
+ public List excludeList = new ArrayList<>();
+
+ @Override
+ public SaReactorFilter addInclude(String... paths) {
+ includeList.addAll(Arrays.asList(paths));
+ return this;
+ }
+
+ @Override
+ public SaReactorFilter addExclude(String... paths) {
+ excludeList.addAll(Arrays.asList(paths));
+ return this;
+ }
+
+ @Override
+ public SaReactorFilter setIncludeList(List pathList) {
+ includeList = pathList;
+ return this;
+ }
+
+ @Override
+ public SaReactorFilter 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 SaReactorFilter setAuth(SaFilterAuthStrategy auth) {
+ this.auth = auth;
+ return this;
+ }
+
+ @Override
+ public SaReactorFilter setError(SaFilterErrorStrategy error) {
+ this.error = error;
+ return this;
+ }
+
+ @Override
+ public SaReactorFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
+ this.beforeAuth = beforeAuth;
+ return this;
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ // ---------- 全局认证处理
+ try {
+ SaReactorSyncHolder.setContext(exchange);
+ beforeAuth.run(null);
+ SaRouter.match(includeList).notMatch(excludeList).check(r -> auth.run(null));
+ }
+ catch (StopMatchException ignored) {}
+ catch (BackResultException e) {
+ return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
+ }
+ catch (Throwable e) {
+ return SaReactorOperateUtil.writeResult(exchange, String.valueOf(error.run(e)));
+ }
+ finally {
+ SaReactorSyncHolder.clearContext();
+ }
+
+ return chain.filter(exchange);
+ }
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..279368372771ad65671577f62a6aa8292541acfc
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.reactor.filter;
+
+import cn.dev33.satoken.reactor.context.SaReactorHolder;
+import cn.dev33.satoken.util.SaTokenConsts;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * SaTokenContext 上下文初始化过滤器 (基于 Reactor)
+ *
+ * @author click33
+ * @since 1.42.0
+ */
+@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER)
+public class SaTokenContextFilterForReactor implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ return chain.filter(exchange)
+ .contextWrite(ctx -> SaReactorHolder.setContext(ctx, exchange, chain))
+ .doFinally(r -> {
+ // 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenCorsFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenCorsFilterForReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..65a2b985ef341ec3f3d9b18ea5fbdfe1d7a04722
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenCorsFilterForReactor.java
@@ -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.reactor.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.reactor.context.SaReactorSyncHolder;
+import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
+import cn.dev33.satoken.strategy.SaStrategy;
+import cn.dev33.satoken.util.SaTokenConsts;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * CORS 跨域策略过滤器 (基于 Reactor)
+ *
+ * @author click33
+ * @since 1.42.0
+ */
+@Order(SaTokenConsts.CORS_FILTER_ORDER)
+public class SaTokenCorsFilterForReactor implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+
+ try {
+ SaReactorSyncHolder.setContext(exchange);
+ SaTokenContextModelBox box = SaHolder.getContext().getModelBox();
+ SaStrategy.instance.corsHandle.execute(box.getRequest(), box.getResponse(), box.getStorage());
+ }
+ catch (StopMatchException ignored) {}
+ catch (BackResultException e) {
+ return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
+ }
+ finally {
+ SaReactorSyncHolder.clearContext();
+ }
+
+ return chain.filter(exchange);
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce2e42750566b6863d28405e0efc02d983d9a825
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java
@@ -0,0 +1,196 @@
+/*
+ * 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.reactor.model;
+
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.application.ApplicationInfo;
+import cn.dev33.satoken.context.model.SaRequest;
+import cn.dev33.satoken.reactor.context.SaReactorHolder;
+import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
+import cn.dev33.satoken.util.SaFoxUtil;
+import org.springframework.http.HttpCookie;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilterChain;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * 对 SaRequest 包装类的实现(Reactor 响应式编程版)
+ *
+ * @author click33
+ * @since 1.34.0
+ */
+public class SaRequestForReactor implements SaRequest {
+
+ /**
+ * 底层Request对象
+ */
+ protected ServerHttpRequest request;
+
+ /**
+ * 实例化
+ * @param request request对象
+ */
+ public SaRequestForReactor(ServerHttpRequest request) {
+ this.request = request;
+ }
+
+ /**
+ * 获取底层源对象
+ */
+ @Override
+ public Object getSource() {
+ return request;
+ }
+
+ /**
+ * 在 [请求体] 里获取一个值
+ */
+ @Override
+ public String getParam(String name) {
+ return request.getQueryParams().getFirst(name);
+ }
+
+ /**
+ * 获取 [请求体] 里提交的所有参数名称
+ * @return 参数名称列表
+ */
+ @Override
+ public Collection getParamNames(){
+ return request.getQueryParams().keySet();
+ }
+
+ /**
+ * 获取 [请求体] 里提交的所有参数
+ * @return 参数列表
+ */
+ @Override
+ public Map getParamMap(){
+ return request.getQueryParams().toSingleValueMap();
+ }
+
+ /**
+ * 在 [请求头] 里获取一个值
+ */
+ @Override
+ public String getHeader(String name) {
+ return request.getHeaders().getFirst(name);
+ }
+
+ /**
+ * 在 [Cookie作用域] 里获取一个值
+ */
+ @Override
+ public String getCookieValue(String name) {
+ return getCookieLastValue(name);
+ }
+
+ /**
+ * 在 [ Cookie作用域 ] 里获取一个值 (第一个此名称的)
+ */
+ @Override
+ public String getCookieFirstValue(String name){
+ HttpCookie cookie = request.getCookies().getFirst(name);
+ if(cookie == null) {
+ return null;
+ }
+ return cookie.getValue();
+ }
+
+ /**
+ * 在 [ Cookie作用域 ] 里获取一个值 (最后一个此名称的)
+ * @param name 键
+ * @return 值
+ */
+ @Override
+ public String getCookieLastValue(String name){
+ String value = null;
+ String cookieStr = getHeader("Cookie");
+ if(SaFoxUtil.isNotEmpty(cookieStr)) {
+ String[] cookieItems = cookieStr.split(";");
+ for (String item : cookieItems) {
+ String[] kv = item.split("=");
+ if (kv.length == 2) {
+ if (kv[0].trim().equals(name)) {
+ value = kv[1].trim();
+ }
+ }
+ }
+ }
+ return value;
+
+ // 此种写法无法获取到最后一个 Cookie,WebFlux 底层代码应该是有bug,前端提交多个同名Cookie时只能解析出第一个来
+ // List cookies = request.getCookies().get(name);
+ // if(cookies.isEmpty()) {
+ // return null;
+ // }
+ // return cookies.get(cookies.size() - 1).getValue();
+ }
+
+ /**
+ * 返回当前请求path (不包括上下文名称)
+ */
+ @Override
+ public String getRequestPath() {
+ return ApplicationInfo.cutPathPrefix(request.getPath().toString());
+ }
+
+ /**
+ * 返回当前请求的url,例:http://xxx.com/test
+ * @return see note
+ */
+ public String getUrl() {
+ String currDomain = SaManager.getConfig().getCurrDomain();
+ if( ! SaFoxUtil.isEmpty(currDomain)) {
+ return currDomain + this.getRequestPath();
+ }
+ return request.getURI().toString();
+ }
+
+ /**
+ * 返回当前请求的类型
+ */
+ @Override
+ public String getMethod() {
+ return request.getMethod().name();
+ }
+
+ /**
+ * 查询请求 host
+ */
+ @Override
+ public String getHost() {
+ return request.getURI().getHost();
+ }
+
+ /**
+ * 转发请求
+ */
+ @Override
+ public Object forward(String path) {
+ ServerWebExchange exchange = SaReactorSyncHolder.getExchange();
+ WebFilterChain chain = exchange.getAttribute(SaReactorHolder.CHAIN_KEY);
+
+ ServerHttpRequest newRequest = request.mutate().path(path).build();
+ ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
+
+ return chain.filter(newExchange);
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaResponseForReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaResponseForReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fb5ac8687d3818b9d4fbf08f1de7cd710cbd30b
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaResponseForReactor.java
@@ -0,0 +1,93 @@
+/*
+ * 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.reactor.model;
+
+import java.net.URI;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+
+import cn.dev33.satoken.context.model.SaResponse;
+
+/**
+ * 对 SaResponse 包装类的实现(Reactor 响应式编程版)
+ *
+ * @author click33
+ * @since 1.34.0
+ */
+public class SaResponseForReactor implements SaResponse {
+
+ /**
+ * 底层Response对象
+ */
+ protected ServerHttpResponse response;
+
+ /**
+ * 实例化
+ * @param response response对象
+ */
+ public SaResponseForReactor(ServerHttpResponse response) {
+ this.response = response;
+ }
+
+ /**
+ * 获取底层源对象
+ */
+ @Override
+ public Object getSource() {
+ return response;
+ }
+
+ /**
+ * 设置响应状态码
+ */
+ @Override
+ public SaResponse setStatus(int sc) {
+ response.setStatusCode(HttpStatus.valueOf(sc));
+ return this;
+ }
+
+ /**
+ * 在响应头里写入一个值
+ */
+ @Override
+ public SaResponse setHeader(String name, String value) {
+ response.getHeaders().set(name, value);
+ return this;
+ }
+
+ /**
+ * 在响应头里添加一个值
+ * @param name 名字
+ * @param value 值
+ * @return 对象自身
+ */
+ public SaResponse addHeader(String name, String value) {
+ response.getHeaders().add(name, value);
+ return this;
+ }
+
+ /**
+ * 重定向
+ */
+ @Override
+ public Object redirect(String url) {
+ response.setStatusCode(HttpStatus.FOUND);
+ response.getHeaders().setLocation(URI.create(url));
+ return null;
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaStorageForReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaStorageForReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..55e35ea89a21ed916d43344ae504e4120baee8a7
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaStorageForReactor.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.reactor.model;
+
+import org.springframework.web.server.ServerWebExchange;
+
+import cn.dev33.satoken.context.model.SaStorage;
+
+/**
+ * 对 SaStorage 包装类的实现(Reactor 响应式编程版)
+ *
+ * @author click33
+ * @since 1.34.0
+ */
+public class SaStorageForReactor implements SaStorage {
+
+ /**
+ * 底层 ServerWebExchange 对象
+ */
+ protected ServerWebExchange exchange;
+
+ /**
+ * 实例化
+ * @param exchange exchange对象
+ */
+ public SaStorageForReactor(ServerWebExchange exchange) {
+ this.exchange = exchange;
+ }
+
+ /**
+ * 获取底层源对象
+ */
+ @Override
+ public Object getSource() {
+ return exchange;
+ }
+
+ /**
+ * 在 [Request作用域] 里写入一个值
+ */
+ @Override
+ public SaStorageForReactor set(String key, Object value) {
+ exchange.getAttributes().put(key, value);
+ return this;
+ }
+
+ /**
+ * 在 [Request作用域] 里获取一个值
+ */
+ @Override
+ public Object get(String key) {
+ return exchange.getAttributes().get(key);
+ }
+
+ /**
+ * 在 [Request作用域] 里删除一个值
+ */
+ @Override
+ public SaStorageForReactor delete(String key) {
+ exchange.getAttributes().remove(key);
+ return this;
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/package-info.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..df78335d6f7b36ec599cb6b50bba74dbbd5d8a01
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/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 集成 Reactor 响应式编程的各个组件
+ */
+package cn.dev33.satoken.reactor;
\ No newline at end of file
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b4e785a3341078310ea3bfae3839f93faf99e11
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.reactor.spring;
+
+import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
+
+/**
+ * 此为低版本(<1.42.0) 的上下文处理方案,仅做留档,如无必要请勿使用
+ *
+ * Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制
+ *
+ * @author click33
+ * @since 1.33.0
+ */
+public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal {
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java
new file mode 100644
index 0000000000000000000000000000000000000000..559be296a8331ed3984e9eae7bb06531a4d954a5
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java
@@ -0,0 +1,70 @@
+/*
+ * 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.reactor.spring;
+
+import cn.dev33.satoken.reactor.filter.SaFirewallCheckFilterForReactor;
+import cn.dev33.satoken.reactor.filter.SaTokenContextFilterForReactor;
+import cn.dev33.satoken.reactor.filter.SaTokenCorsFilterForReactor;
+import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
+import cn.dev33.satoken.strategy.SaStrategy;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 注册 Sa-Token 所需要的 Bean
+ *
+ * @author click33
+ * @since 1.34.0
+ */
+public class SaTokenContextRegister {
+
+ public SaTokenContextRegister() {
+ // 重写路由匹配算法
+ SaStrategy.instance.routeMatcher = (pattern, path) -> {
+ return SaPathPatternParserUtil.match(pattern, path);
+ };
+ }
+
+ /**
+ * 上下文过滤器
+ *
+ * @return /
+ */
+ @Bean
+ public SaTokenContextFilterForReactor saTokenContextFilterForServlet() {
+ return new SaTokenContextFilterForReactor();
+ }
+
+ /**
+ * CORS 跨域策略过滤器
+ *
+ * @return /
+ */
+ @Bean
+ public SaTokenCorsFilterForReactor saTokenCorsFilterForReactor() {
+ return new SaTokenCorsFilterForReactor();
+ }
+
+ /**
+ * 防火墙过滤器
+ *
+ * @return /
+ */
+ @Bean
+ public SaFirewallCheckFilterForReactor saFirewallCheckFilterForReactor() {
+ return new SaFirewallCheckFilterForReactor();
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..706501bf568304d1f95730c2190d809b42b80c71
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java
@@ -0,0 +1,46 @@
+/*
+ * 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.reactor.util;
+
+import cn.dev33.satoken.util.SaTokenConsts;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * Reactor 操作工具类
+ *
+ * @author click33
+ * @since 1.42.0
+ */
+public class SaReactorOperateUtil {
+
+ /**
+ * 写入结果到输出流
+ * @param exchange /
+ * @param result /
+ * @return /
+ */
+ public static Mono writeResult(ServerWebExchange exchange, String result) {
+ // 写入输出流
+ // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
+ // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
+ if(exchange.getResponse().getHeaders().getFirst(SaTokenConsts.CONTENT_TYPE_KEY) == null) {
+ exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
+ }
+ return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(result.getBytes())));
+ }
+
+}
diff --git a/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000000000000000000000000000000000000..325771974bbf8efcc8015e0b2d0999792b6c72f1
--- /dev/null
+++ b/sa-token-starter/sa-token-reactor-spring-cloud3-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+cn.dev33.satoken.reactor.spring.SaTokenContextRegister
\ No newline at end of file