Api config refactor
This commit is contained in:
@@ -44,7 +44,7 @@ public final class Consts {
|
||||
public static final char SINGLE_QUOTE = '\'';
|
||||
public static final char ASTERISK = '*';
|
||||
public static final char DASH = '-';
|
||||
public static final char UNDERLINE = '_';
|
||||
public static final char UNDER_LINE = '_';
|
||||
public static final char EQUAL = '=';
|
||||
public static final char AT = '@';
|
||||
public static final char LEFT_SQUARE_BRACKET = '[';
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -34,9 +35,11 @@ import we.plugin.auth.AuthPluginFilter;
|
||||
import we.plugin.stat.StatPluginFilter;
|
||||
import we.proxy.Route;
|
||||
import we.util.ReactorUtils;
|
||||
import we.util.Result;
|
||||
import we.util.WebUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -74,23 +77,28 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
return process(exchange, chain, eas, vm);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// TODO: improve
|
||||
private Mono<Void> process(ServerWebExchange exchange, WebFilterChain chain, Map<String, Object> eas, Mono vm) {
|
||||
return chain(exchange, vm, authPluginFilter).defaultIfEmpty(ReactorUtils.NULL)
|
||||
.flatMap(
|
||||
v -> {
|
||||
Object authRes = WebUtils.getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
Result<ApiConfig> authRes = (Result<ApiConfig>) WebUtils.getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (authRes.code == Result.FAIL) {
|
||||
return WebUtils.responseError(exchange, HttpStatus.FORBIDDEN.value(), authRes.msg);
|
||||
}
|
||||
Mono m = ReactorUtils.getInitiateMono();
|
||||
if (authRes instanceof ApiConfig) {
|
||||
ApiConfig ac = (ApiConfig) authRes;
|
||||
|
||||
ApiConfig ac = authRes.data;
|
||||
if (ac == null) {
|
||||
afterAuth(exchange, null, null);
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
}
|
||||
Route route = ac.getRoute(exchange);
|
||||
exchange.getAttributes().put(WebUtils.ROUTE, route);
|
||||
|
||||
eas.put(WebUtils.ROUTE, route);
|
||||
afterAuth(exchange, ac, route);
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
m = m.defaultIfEmpty(ReactorUtils.NULL);
|
||||
if (route.pluginConfigs == null || route.pluginConfigs.isEmpty()) {
|
||||
if (CollectionUtils.isEmpty(route.pluginConfigs)) {
|
||||
return m.flatMap(func(exchange, chain));
|
||||
} else {
|
||||
return m.flatMap(
|
||||
@@ -100,20 +108,6 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (authRes == ApiConfigService.Access.YES) {
|
||||
afterAuth(exchange, null, null);
|
||||
m = executeFixedPluginFilters(exchange);
|
||||
return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
} else {
|
||||
String err = null;
|
||||
if (authRes instanceof ApiConfigService.Access) {
|
||||
ApiConfigService.Access access = (ApiConfigService.Access) authRes;
|
||||
err = access.getReason();
|
||||
} else {
|
||||
err = authRes.toString();
|
||||
}
|
||||
return WebUtils.responseError(exchange, HttpStatus.FORBIDDEN.value(), err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class FizzPluginFilterChain {
|
||||
public static Mono<Void> next(ServerWebExchange exchange) {
|
||||
Iterator<PluginConfig> it = exchange.getAttribute(pluginConfigsIt);
|
||||
if (it == null) {
|
||||
List<PluginConfig> pcs = WebUtils.getApiConfig(exchange).pluginConfigs;
|
||||
List<PluginConfig> pcs = WebUtils.getRoute(exchange).pluginConfigs;
|
||||
it = pcs.iterator();
|
||||
Map<String, Object> attris = exchange.getAttributes();
|
||||
attris.put(pluginConfigsIt, it);
|
||||
|
||||
@@ -30,13 +30,13 @@ import java.util.Map;
|
||||
|
||||
public class PluginConfig {
|
||||
|
||||
public static final String CUSTOM_CONFIG = "$fc";
|
||||
public static final String CUSTOM_CONFIG = "fcK";
|
||||
|
||||
public String plugin; // tb_plugin.eng_name
|
||||
|
||||
public String fixedConfig;
|
||||
|
||||
public Map<String/*tb_api_plugin_config.item*/, Object/*tb_api_plugin_config.value*/> config = Collections.EMPTY_MAP;
|
||||
public Map<String/*tb_api_plugin_config.item*/, Object/*tb_api_plugin_config.value*/> config = Collections.emptyMap();
|
||||
|
||||
// @JsonProperty(value = "config", access = JsonProperty.Access.WRITE_ONLY)
|
||||
public void setConfig(String confJson) {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package we.plugin.auth;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.util.Result;
|
||||
import we.util.Utils;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
public abstract class AbstractCustomAuth implements CustomAuth {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public Mono<ApiConfigService.Access> auth(ServerWebExchange exchange, String appId, String ip, String timestamp, String sign, App fizzAppConfig) {
|
||||
throw Utils.runtimeExceptionWithoutStack("don't implement me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if authentication pass then Result.code = Result.SUCC, otherwise Result.code = Result.FAIL
|
||||
*/
|
||||
public abstract Mono<Result<?>> auth(String appId, String ip, String timestamp, String sign, App fizzAppConfig, ServerWebExchange exchange);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class ApiConfig {
|
||||
|
||||
public static final char ALLOW = 'a';
|
||||
|
||||
public static final char FORBID = 'f';
|
||||
// public static final char FORBID = 'f';
|
||||
|
||||
public static final String ALL_METHOD = "AM";
|
||||
|
||||
@@ -62,25 +62,36 @@ public class ApiConfig {
|
||||
|
||||
private static final int ENABLE = 1;
|
||||
|
||||
private static final int UNABLE = 0;
|
||||
// private static final int UNABLE = 0;
|
||||
|
||||
@JsonProperty(
|
||||
access = JsonProperty.Access.WRITE_ONLY
|
||||
)
|
||||
public int id; // tb_api_auth.id
|
||||
|
||||
@JsonProperty(
|
||||
access = JsonProperty.Access.WRITE_ONLY
|
||||
)
|
||||
public int isDeleted = 0; // tb_api_auth.is_deleted
|
||||
|
||||
public Set<String> gatewayGroups = Stream.of(GatewayGroup.DEFAULT).collect(Collectors.toSet());
|
||||
|
||||
public String service; // a
|
||||
public String service;
|
||||
|
||||
public String backendService;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
@JsonProperty(
|
||||
access = JsonProperty.Access.WRITE_ONLY
|
||||
)
|
||||
public HttpMethod method;
|
||||
|
||||
public Object fizzMethod = ALL_METHOD;
|
||||
|
||||
public String path = match_all;
|
||||
|
||||
@JsonProperty(
|
||||
access = JsonProperty.Access.WRITE_ONLY
|
||||
)
|
||||
public boolean exactMatch = false;
|
||||
|
||||
public String backendPath;
|
||||
|
||||
@@ -25,7 +25,9 @@ import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -41,6 +43,7 @@ import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
@@ -51,11 +54,7 @@ public class ApiConfigService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiConfigService.class);
|
||||
|
||||
private static final String mpps = "$mpps";
|
||||
|
||||
private static final String deny = "route which match current request can't be access by client app, or is not exposed to current gateway group";
|
||||
|
||||
public static final String AUTH_MSG = "$authMsg";
|
||||
private static final String macs = "macsT";
|
||||
|
||||
public Map<String, ServiceConfig> serviceConfigMap = new HashMap<>(128);
|
||||
|
||||
@@ -112,11 +111,13 @@ public class ApiConfigService {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: no need like this
|
||||
public void refreshLocalCache() throws Throwable {
|
||||
this.init(null);
|
||||
initPlugin();
|
||||
}
|
||||
|
||||
// TODO: no need like this
|
||||
private void init(Supplier<Mono<Throwable>> doAfterLoadCache) throws Throwable {
|
||||
Map<Integer, ApiConfig> apiConfigMapTmp = new HashMap<>(128);
|
||||
Map<String, ServiceConfig> serviceConfigMapTmp = new HashMap<>(128);
|
||||
@@ -129,7 +130,7 @@ public class ApiConfigService {
|
||||
return Flux.just(e);
|
||||
}
|
||||
Object v = e.getValue();
|
||||
log.info("api config: " + v.toString(), LogService.BIZ_ID, k.toString());
|
||||
log.info("get api config data: {}", v.toString(), LogService.BIZ_ID, k.toString());
|
||||
String json = (String) v;
|
||||
try {
|
||||
ApiConfig ac = JacksonUtils.readValue(json, ApiConfig.class);
|
||||
@@ -138,7 +139,7 @@ public class ApiConfigService {
|
||||
return Flux.just(e);
|
||||
} catch (Throwable t) {
|
||||
throwable[0] = t;
|
||||
log.info(json, t);
|
||||
log.error("deser {}", json, t);
|
||||
return Flux.error(t);
|
||||
}
|
||||
}).blockLast())).flatMap(
|
||||
@@ -146,7 +147,6 @@ public class ApiConfigService {
|
||||
if (throwable[0] != null) {
|
||||
return Mono.error(throwable[0]);
|
||||
}
|
||||
|
||||
if (doAfterLoadCache != null) {
|
||||
return doAfterLoadCache.get();
|
||||
} else {
|
||||
@@ -157,26 +157,27 @@ public class ApiConfigService {
|
||||
if (error != ReactorUtils.EMPTY_THROWABLE) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.apiConfigMap = apiConfigMapTmp;
|
||||
this.serviceConfigMap = serviceConfigMapTmp;
|
||||
}
|
||||
|
||||
// TODO: no need like this
|
||||
private Mono<Throwable> lsnApiConfigChange() {
|
||||
final Throwable[] throwable = new Throwable[1];
|
||||
final boolean[] b = {false};
|
||||
rt.listenToChannel(apiConfigServiceProperties.getFizzApiConfigChannel()).doOnError(t -> {
|
||||
String ch = apiConfigServiceProperties.getFizzApiConfigChannel();
|
||||
rt.listenToChannel(ch).doOnError(t -> {
|
||||
throwable[0] = t;
|
||||
b[0] = false;
|
||||
log.error("lsn " + apiConfigServiceProperties.getFizzApiConfigChannel(), t);
|
||||
log.error("lsn {}", ch, t);
|
||||
}).doOnSubscribe(
|
||||
s -> {
|
||||
b[0] = true;
|
||||
log.info("success to lsn on " + apiConfigServiceProperties.getFizzApiConfigChannel());
|
||||
log.info("success to lsn on {}", ch);
|
||||
}
|
||||
).doOnNext(msg -> {
|
||||
String json = msg.getMessage();
|
||||
log.info(json, LogService.BIZ_ID, "acc" + System.currentTimeMillis());
|
||||
log.info("api config change: {}", json, LogService.BIZ_ID, "acc" + System.currentTimeMillis());
|
||||
try {
|
||||
ApiConfig ac = JacksonUtils.readValue(json, ApiConfig.class);
|
||||
ApiConfig r = apiConfigMap.remove(ac.id);
|
||||
@@ -191,7 +192,7 @@ public class ApiConfigService {
|
||||
apiConifg2appsService.remove(ac.id);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.info(json, t);
|
||||
log.error("deser {}", json, t);
|
||||
}
|
||||
}).subscribe();
|
||||
Throwable t = throwable[0];
|
||||
@@ -295,14 +296,11 @@ public class ApiConfigService {
|
||||
private void updateServiceConfigMap(ApiConfig ac, Map<String, ServiceConfig> serviceConfigMap) {
|
||||
ServiceConfig sc = serviceConfigMap.get(ac.service);
|
||||
if (ac.isDeleted == ApiConfig.DELETED) {
|
||||
if (sc == null) {
|
||||
log.info("no " + ac.service + " config to delete");
|
||||
} else {
|
||||
if (sc != null) {
|
||||
sc.remove(ac);
|
||||
if (sc.path2methodToApiConfigMapMap.isEmpty()) {
|
||||
if (sc.apiConfigMap.isEmpty()) {
|
||||
serviceConfigMap.remove(ac.service);
|
||||
}
|
||||
// apiConifg2appsService.remove(ac.id);
|
||||
}
|
||||
} else {
|
||||
if (sc == null) {
|
||||
@@ -315,6 +313,9 @@ public class ApiConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public enum Access {
|
||||
|
||||
YES (null),
|
||||
@@ -344,151 +345,164 @@ public class ApiConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
public ApiConfig getApiConfig(String app, String service, HttpMethod method, String path) {
|
||||
ApiConfig ac = null;
|
||||
for (String g : gatewayGroupService.currentGatewayGroupSet) {
|
||||
ac = getApiConfig(service, method, path, g, app);
|
||||
if (ac != null) {
|
||||
return ac;
|
||||
}
|
||||
}
|
||||
return ac;
|
||||
public Result<ApiConfig> getApiConfig(String app, String service, HttpMethod method, String path) {
|
||||
return getApiConfig(null, app, service, method, path);
|
||||
}
|
||||
|
||||
public ApiConfig getApiConfig(String service, HttpMethod method, String path, String gatewayGroup, String app) {
|
||||
public Result<ApiConfig> getApiConfig(Set<String> gatewayGroups, String app, String service, HttpMethod method, String path) {
|
||||
ServiceConfig sc = serviceConfigMap.get(service);
|
||||
if (sc != null) {
|
||||
List<ApiConfig> apiConfigs = sc.getApiConfigs(method, path, gatewayGroup);
|
||||
if (!apiConfigs.isEmpty()) {
|
||||
List<String> matchPathPatterns = ThreadContext.getArrayList(mpps);
|
||||
if (sc == null) {
|
||||
return Result.fail("no " + service + " config");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(gatewayGroups)) {
|
||||
gatewayGroups = gatewayGroupService.currentGatewayGroupSet;
|
||||
}
|
||||
List<ApiConfig> apiConfigs = sc.getApiConfigs(gatewayGroups, method, path);
|
||||
if (apiConfigs.isEmpty()) {
|
||||
return Result.fail(service + " don't have api config matching " + gatewayGroups + " group " + method + " method " + path + " path");
|
||||
}
|
||||
List<ApiConfig> appCanAccess = ThreadContext.getArrayList(macs);
|
||||
for (int i = 0; i < apiConfigs.size(); i++) {
|
||||
ApiConfig ac = apiConfigs.get(i);
|
||||
if (ac.checkApp) {
|
||||
if (apiConifg2appsService.contains(ac.id, app)) {
|
||||
matchPathPatterns.add(ac.path);
|
||||
if (StringUtils.isNotBlank(app) && apiConifg2appsService.contains(ac.id, app)) {
|
||||
appCanAccess.add(ac);
|
||||
}
|
||||
} else {
|
||||
matchPathPatterns.add(ac.path);
|
||||
appCanAccess.add(ac);
|
||||
}
|
||||
}
|
||||
if (matchPathPatterns.isEmpty()) {
|
||||
if (app == null) {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, "no app msg in req");
|
||||
} else {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, app + " not in app whitelist of routes which match " + gatewayGroup + ' ' + service + ' ' + method + ' ' + path);
|
||||
}
|
||||
}
|
||||
if (!matchPathPatterns.isEmpty()) {
|
||||
if (matchPathPatterns.size() > 1) {
|
||||
Collections.sort(matchPathPatterns, UrlTransformUtils.ANT_PATH_MATCHER.getPatternComparator(path));
|
||||
}
|
||||
String bestPathPattern = matchPathPatterns.get(0);
|
||||
for (int i = 0; i < apiConfigs.size(); i++) {
|
||||
ApiConfig ac = apiConfigs.get(i);
|
||||
if (StringUtils.equals(ac.path, bestPathPattern)) {
|
||||
return ac;
|
||||
}
|
||||
if (appCanAccess.isEmpty()) {
|
||||
return Result.fail("app " + app + " can't access " + JacksonUtils.writeValueAsString(apiConfigs));
|
||||
}
|
||||
ApiConfig bestOne = appCanAccess.get(0);
|
||||
if (appCanAccess.size() != 1) {
|
||||
appCanAccess.sort(new ApiConfigPathPatternComparator(path));
|
||||
ApiConfig ac0 = appCanAccess.get(0);
|
||||
bestOne = ac0;
|
||||
ApiConfig ac1 = appCanAccess.get(1);
|
||||
if (ac0.path.equals(ac1.path)) {
|
||||
if (ac0.fizzMethod == ac1.fizzMethod) {
|
||||
if (StringUtils.isNotBlank(app)) {
|
||||
if (!ac0.checkApp) {
|
||||
bestOne = ac1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, "no " + service + " service config");
|
||||
if (ac0.fizzMethod == ApiConfig.ALL_METHOD) {
|
||||
bestOne = ac1;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.succ(bestOne);
|
||||
}
|
||||
|
||||
public Mono<Object> canAccess(ServerWebExchange exchange) {
|
||||
public Mono<Result<ApiConfig>> auth(ServerWebExchange exchange) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
HttpHeaders hdrs = req.getHeaders();
|
||||
LogService.setBizId(WebUtils.getTraceId(exchange));
|
||||
return canAccess(exchange, WebUtils.getAppId(exchange), WebUtils.getOriginIp(exchange), getTimestamp(hdrs), getSign(hdrs),
|
||||
return auth(exchange, WebUtils.getAppId(exchange), WebUtils.getOriginIp(exchange), getTimestamp(hdrs), getSign(hdrs),
|
||||
WebUtils.getClientService(exchange), req.getMethod(), WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
|
||||
// TODO: improve ...
|
||||
private Mono<Object> canAccess(ServerWebExchange exchange, String app, String ip, String timestamp, String sign, String service, HttpMethod method, String path) {
|
||||
private Mono<Result<ApiConfig>> auth(ServerWebExchange exchange, String app, String ip, String timestamp, String sign, String service, HttpMethod method, String path) {
|
||||
|
||||
if (!systemConfig.isAggregateTestAuth()) {
|
||||
if (SystemConfig.DEFAULT_GATEWAY_TEST_PREFIX0.equals(WebUtils.getClientReqPathPrefix(exchange))) {
|
||||
return Mono.just(Access.YES);
|
||||
return Mono.just(Result.succ());
|
||||
}
|
||||
}
|
||||
|
||||
ApiConfig ac = getApiConfig(app, service, method, path);
|
||||
if (ac == null) {
|
||||
String authMsg = (String) ThreadContext.remove(AUTH_MSG);
|
||||
if (authMsg == null) {
|
||||
authMsg = deny;
|
||||
}
|
||||
if (!apiConfigServiceProperties.isNeedAuth()) {
|
||||
return Mono.just(Access.YES);
|
||||
Result<ApiConfig> r = getApiConfig(app, service, method, path);
|
||||
if (r.code == Result.FAIL) {
|
||||
if (apiConfigServiceProperties.isNeedAuth()) {
|
||||
return Mono.just(r);
|
||||
} else {
|
||||
return logAndResult(authMsg);
|
||||
return Mono.just(Result.succ());
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ac.checkApp) {
|
||||
ApiConfig ac = r.data;
|
||||
if (ac.checkApp) {
|
||||
App a = appService.getApp(app);
|
||||
if (a.useWhiteList && !a.allow(ip)) {
|
||||
return logAndResult(ip + " not in " + app + " white list", Access.IP_NOT_IN_WHITE_LIST);
|
||||
} else if (a.useAuth) {
|
||||
r.code = Result.FAIL;
|
||||
r.msg = ip + " not in " + app + " app white list";
|
||||
return Mono.just(r);
|
||||
}
|
||||
if (a.useAuth) {
|
||||
if (a.authType == App.AUTH_TYPE.SIGN) {
|
||||
return authSign(ac, a, timestamp, sign);
|
||||
} else if (a.authType == App.AUTH_TYPE.SECRETKEY) {
|
||||
return authSecretkey(ac, a, sign);
|
||||
return authSign(a, timestamp, sign, r);
|
||||
} else if (a.authType == App.AUTH_TYPE.SECRET_KEY) {
|
||||
return authSecretKey(a, sign, r);
|
||||
} else if (customAuth == null) {
|
||||
return logAndResult(app + " no custom auth", Access.NO_CUSTOM_AUTH);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = "no custom auth bean for " + app;
|
||||
return Mono.just(r);
|
||||
} else {
|
||||
return customAuth.auth(exchange, app, ip, timestamp, sign, a).flatMap(v -> {
|
||||
if (customAuth instanceof AbstractCustomAuth) {
|
||||
AbstractCustomAuth abstractCustomAuth = (AbstractCustomAuth) customAuth;
|
||||
return abstractCustomAuth.auth(app, ip, timestamp, sign, a, exchange)
|
||||
.flatMap(
|
||||
res -> {
|
||||
if (res.code == Result.FAIL) {
|
||||
r.code = res.code;
|
||||
r.msg = res.msg;
|
||||
}
|
||||
return Mono.just(r);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return customAuth.auth(exchange, app, ip, timestamp, sign, a)
|
||||
.flatMap(
|
||||
v -> {
|
||||
if (v == Access.YES) {
|
||||
return Mono.just(ac);
|
||||
return Mono.just(r);
|
||||
} else {
|
||||
return Mono.just(Access.CUSTOM_AUTH_REJECT);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = v.getReason();
|
||||
return Mono.just(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Mono.just(ac);
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Mono.just(r);
|
||||
}
|
||||
|
||||
} else {
|
||||
return Mono.just(ac);
|
||||
}
|
||||
}
|
||||
|
||||
private Mono authSign(ApiConfig ac, App a, String timestamp, String sign) {
|
||||
private Mono<Result<ApiConfig>> authSign(App a, String timestamp, String sign, Result<ApiConfig> r) {
|
||||
if (StringUtils.isAnyBlank(timestamp, sign)) {
|
||||
return logAndResult(a.app + " lack timestamp " + timestamp + " or sign " + sign, Access.NO_TIMESTAMP_OR_SIGN);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = a.app + " not present timestamp " + timestamp + " or sign " + sign;
|
||||
} else if (validate(a.app, timestamp, a.secretkey, sign)) {
|
||||
return Mono.just(ac);
|
||||
} else {
|
||||
return logAndResult(a.app + " sign " + sign + " invalid", Access.SIGN_INVALID);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = a.app + " sign " + sign + " invalid";
|
||||
}
|
||||
return Mono.just(r);
|
||||
}
|
||||
|
||||
private boolean validate(String app, String timestamp, String secretKey, String sign) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
b.append(app).append(Consts.S.UNDERLINE).append(timestamp).append(Consts.S.UNDERLINE).append(secretKey);
|
||||
b.append(app) .append(Consts.S.UNDER_LINE)
|
||||
.append(timestamp).append(Consts.S.UNDER_LINE)
|
||||
.append(secretKey);
|
||||
return sign.equalsIgnoreCase(DigestUtils.md532(b.toString()));
|
||||
}
|
||||
|
||||
private Mono authSecretkey(ApiConfig ac, App a, String sign) {
|
||||
private Mono<Result<ApiConfig>> authSecretKey(App a, String sign, Result<ApiConfig> r) {
|
||||
if (StringUtils.isBlank(sign)) {
|
||||
return logAndResult(a.app + " lack secretkey " + sign, Access.NO_SECRETKEY);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = a.app + " not present secret key " + sign;
|
||||
} else if (a.secretkey.equals(sign)) {
|
||||
return Mono.just(ac);
|
||||
} else {
|
||||
return logAndResult(a.app + " secretkey " + sign + " invalid", Access.SECRETKEY_INVALID);
|
||||
r.code = Result.FAIL;
|
||||
r.msg = a.app + " secret key " + sign + " invalid";
|
||||
}
|
||||
}
|
||||
|
||||
private Mono logAndResult(String msg, Access access) {
|
||||
log.warn(msg);
|
||||
return Mono.just(access);
|
||||
}
|
||||
|
||||
private Mono logAndResult(String msg) {
|
||||
log.warn(msg);
|
||||
return Mono.just(msg);
|
||||
return Mono.just(r);
|
||||
}
|
||||
|
||||
private String getTimestamp(HttpHeaders reqHdrs) {
|
||||
@@ -512,4 +526,169 @@ public class ApiConfigService {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ApiConfigPathPatternComparator implements Comparator<ApiConfig> {
|
||||
|
||||
private final String path;
|
||||
|
||||
public ApiConfigPathPatternComparator(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ApiConfig ac1, ApiConfig ac2) {
|
||||
String pattern1 = ac1.path, pattern2 = ac2.path;
|
||||
ApiConfigPathPatternComparator.PatternInfo info1 = new ApiConfigPathPatternComparator.PatternInfo(pattern1);
|
||||
ApiConfigPathPatternComparator.PatternInfo info2 = new ApiConfigPathPatternComparator.PatternInfo(pattern2);
|
||||
|
||||
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
|
||||
return 0;
|
||||
}
|
||||
else if (info1.isLeastSpecific()) {
|
||||
return 1;
|
||||
}
|
||||
else if (info2.isLeastSpecific()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean pattern1EqualsPath = pattern1.equals(this.path);
|
||||
boolean pattern2EqualsPath = pattern2.equals(this.path);
|
||||
if (pattern1EqualsPath && pattern2EqualsPath) {
|
||||
return 0;
|
||||
}
|
||||
else if (pattern1EqualsPath) {
|
||||
return -1;
|
||||
}
|
||||
else if (pattern2EqualsPath) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info1.isPrefixPattern() && info2.isPrefixPattern()) {
|
||||
return info2.getLength() - info1.getLength();
|
||||
}
|
||||
else if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
|
||||
return 1;
|
||||
}
|
||||
else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info1.getTotalCount() != info2.getTotalCount()) {
|
||||
return info1.getTotalCount() - info2.getTotalCount();
|
||||
}
|
||||
|
||||
if (info1.getLength() != info2.getLength()) {
|
||||
return info2.getLength() - info1.getLength();
|
||||
}
|
||||
|
||||
if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
|
||||
return -1;
|
||||
}
|
||||
else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info1.getUriVars() < info2.getUriVars()) {
|
||||
return -1;
|
||||
}
|
||||
else if (info2.getUriVars() < info1.getUriVars()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static class PatternInfo {
|
||||
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?}");
|
||||
|
||||
@Nullable
|
||||
private final String pattern;
|
||||
|
||||
private int uriVars;
|
||||
|
||||
private int singleWildcards;
|
||||
|
||||
private int doubleWildcards;
|
||||
|
||||
private boolean catchAllPattern;
|
||||
|
||||
private boolean prefixPattern;
|
||||
|
||||
@Nullable
|
||||
private Integer length;
|
||||
|
||||
public PatternInfo(@Nullable String pattern) {
|
||||
this.pattern = pattern;
|
||||
if (this.pattern != null) {
|
||||
initCounters();
|
||||
this.catchAllPattern = this.pattern.equals("/**");
|
||||
this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
|
||||
}
|
||||
if (this.uriVars == 0) {
|
||||
this.length = (this.pattern != null ? this.pattern.length() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initCounters() {
|
||||
int pos = 0;
|
||||
if (this.pattern != null) {
|
||||
while (pos < this.pattern.length()) {
|
||||
if (this.pattern.charAt(pos) == '{') {
|
||||
this.uriVars++;
|
||||
pos++;
|
||||
}
|
||||
else if (this.pattern.charAt(pos) == '*') {
|
||||
if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
|
||||
this.doubleWildcards++;
|
||||
pos += 2;
|
||||
}
|
||||
else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {
|
||||
this.singleWildcards++;
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getUriVars() {
|
||||
return this.uriVars;
|
||||
}
|
||||
|
||||
public int getSingleWildcards() {
|
||||
return this.singleWildcards;
|
||||
}
|
||||
|
||||
public int getDoubleWildcards() {
|
||||
return this.doubleWildcards;
|
||||
}
|
||||
|
||||
public boolean isLeastSpecific() {
|
||||
return (this.pattern == null || this.catchAllPattern);
|
||||
}
|
||||
|
||||
public boolean isPrefixPattern() {
|
||||
return this.prefixPattern;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
if (this.length == null) {
|
||||
this.length = (this.pattern != null ?
|
||||
VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length() : 0);
|
||||
}
|
||||
return this.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class App {
|
||||
static interface AUTH_TYPE {
|
||||
static final int SIGN = 1;
|
||||
static final int CUSTOM = 2;
|
||||
static final int SECRETKEY = 3;
|
||||
static final int SECRET_KEY = 3;
|
||||
}
|
||||
|
||||
public int isDeleted = 0; // tb_app_auth.is_deleted
|
||||
@@ -61,7 +61,7 @@ public class App {
|
||||
public Map<String, List<String[]>> ips = new HashMap<>();
|
||||
|
||||
public void setUseAuth(int i) {
|
||||
if (i == AUTH_TYPE.SIGN || i == AUTH_TYPE.SECRETKEY || i == AUTH_TYPE.CUSTOM) {
|
||||
if (i == AUTH_TYPE.SIGN || i == AUTH_TYPE.SECRET_KEY || i == AUTH_TYPE.CUSTOM) {
|
||||
useAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
* @apiNote unstable.
|
||||
*/
|
||||
|
||||
@Component(AuthPluginFilter.AUTH_PLUGIN_FILTER)
|
||||
@@ -47,11 +48,11 @@ public class AuthPluginFilter extends PluginFilter {
|
||||
private ApiConfigService apiConfigService;
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
return apiConfigService.canAccess(exchange).flatMap(
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String pluginConfig) {
|
||||
return apiConfigService.auth(exchange).flatMap(
|
||||
r -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("req auth: " + r, LogService.BIZ_ID, WebUtils.getTraceId(exchange));
|
||||
log.debug("req auth: {}", r, LogService.BIZ_ID, WebUtils.getTraceId(exchange));
|
||||
}
|
||||
Map<String, Object> data = Collections.singletonMap(RESULT, r);
|
||||
return WebUtils.transmitSuccessFilterResultAndEmptyMono(exchange, AUTH_PLUGIN_FILTER, data);
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* extend AbstractCustomAuth instead implement this class.
|
||||
*
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
@@ -28,6 +30,8 @@ public interface CustomAuth {
|
||||
|
||||
/**
|
||||
* 认证通过返回 Mono<Access.YES>, 不通过返回 Mono<Access.CUSTOM_AUTH_REJECT>
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
Mono<ApiConfigService.Access> auth(ServerWebExchange exchange, String appId, String ip, String timestamp, String sign, App fizzAppConfig);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.Set;
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
@Deprecated
|
||||
public class GatewayGroup2apiConfig {
|
||||
|
||||
private Map<String/*gg*/, Set<ApiConfig>> configMap = new HashMap<>(8);
|
||||
|
||||
@@ -34,132 +34,123 @@ public class ServiceConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ServiceConfig.class);
|
||||
|
||||
private static final String gg2acs = "$gg2acs";
|
||||
private static final String gmpT = "gmpT";
|
||||
|
||||
private static final String acs = "$acs";
|
||||
private static final String gsmpT = "gsmpT";
|
||||
|
||||
public String id;
|
||||
private String id;
|
||||
|
||||
@JsonIgnore
|
||||
public Map<Integer, ApiConfig> apiConfigMap = new HashMap<>();
|
||||
|
||||
public Map<String, Map<Object, GatewayGroup2apiConfig>> path2methodToApiConfigMapMap = new HashMap<>();
|
||||
public Map<String/*gateway group*/,
|
||||
Map<Object/*method*/,
|
||||
Map<String/*path patten*/, ApiConfig>
|
||||
>
|
||||
>
|
||||
apiConfigMap = new HashMap<>();
|
||||
|
||||
public ServiceConfig(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void add(ApiConfig ac) {
|
||||
apiConfigMap.put(ac.id, ac);
|
||||
Map<Object, GatewayGroup2apiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap == null) {
|
||||
method2apiConfigMap = new HashMap<Object, GatewayGroup2apiConfig>();
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = new GatewayGroup2apiConfig();
|
||||
gatewayGroup2apiConfig.add(ac);
|
||||
method2apiConfigMap.put(ac.fizzMethod, gatewayGroup2apiConfig);
|
||||
path2methodToApiConfigMapMap.put(ac.path, method2apiConfigMap);
|
||||
} else {
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = method2apiConfigMap.get(ac.fizzMethod);
|
||||
if (gatewayGroup2apiConfig == null) {
|
||||
gatewayGroup2apiConfig = new GatewayGroup2apiConfig();
|
||||
method2apiConfigMap.put(ac.fizzMethod, gatewayGroup2apiConfig);
|
||||
for (String gatewayGroup : ac.gatewayGroups) {
|
||||
Map<Object, Map<String, ApiConfig>> method2pathPattenMap = apiConfigMap.get(gatewayGroup);
|
||||
if (method2pathPattenMap == null) {
|
||||
method2pathPattenMap = new HashMap<>();
|
||||
apiConfigMap.put(gatewayGroup, method2pathPattenMap);
|
||||
}
|
||||
gatewayGroup2apiConfig.add(ac);
|
||||
Map<String, ApiConfig> pathPattern2apiConfigMap = method2pathPattenMap.get(ac.fizzMethod);
|
||||
if (pathPattern2apiConfigMap == null) {
|
||||
pathPattern2apiConfigMap = new HashMap<>();
|
||||
method2pathPattenMap.put(ac.fizzMethod, pathPattern2apiConfigMap);
|
||||
}
|
||||
log.info("add " + ac);
|
||||
pathPattern2apiConfigMap.put(ac.path, ac);
|
||||
}
|
||||
log.info("{} service add api config: {}", id, ac);
|
||||
}
|
||||
|
||||
public void remove(ApiConfig ac) {
|
||||
ApiConfig remove = apiConfigMap.remove(ac.id);
|
||||
Map<Object, GatewayGroup2apiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap == null) {
|
||||
log.info("no config to delete for " + ac.service + ' ' + ac.path);
|
||||
} else {
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = method2apiConfigMap.get(ac.fizzMethod);
|
||||
if (gatewayGroup2apiConfig == null) {
|
||||
log.info("no config to delete for " + ac.service + ' ' + ac.fizzMethod + ' ' + ac.path);
|
||||
} else {
|
||||
log.info(id + " remove " + ac);
|
||||
gatewayGroup2apiConfig.remove(ac);
|
||||
if (gatewayGroup2apiConfig.getConfigMap().isEmpty()) {
|
||||
method2apiConfigMap.remove(ac.fizzMethod);
|
||||
if (method2apiConfigMap.isEmpty()) {
|
||||
path2methodToApiConfigMapMap.remove(ac.path);
|
||||
for (String gatewayGroup : ac.gatewayGroups) {
|
||||
Map<Object, Map<String, ApiConfig>> method2pathPattenMap = apiConfigMap.get(gatewayGroup);
|
||||
if (method2pathPattenMap != null) {
|
||||
Map<String, ApiConfig> pathPattern2apiConfigMap = method2pathPattenMap.get(ac.fizzMethod);
|
||||
if (pathPattern2apiConfigMap != null) {
|
||||
pathPattern2apiConfigMap.remove(ac.path);
|
||||
|
||||
if (pathPattern2apiConfigMap.isEmpty()) {
|
||||
method2pathPattenMap.remove(ac.fizzMethod);
|
||||
if (method2pathPattenMap.isEmpty()) {
|
||||
apiConfigMap.remove(gatewayGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("{} service remove api config: {}", id, ac);
|
||||
}
|
||||
|
||||
public void update(ApiConfig ac) {
|
||||
ApiConfig prev = apiConfigMap.put(ac.id, ac);
|
||||
log.info(prev + " is updated by " + ac + " in api config map");
|
||||
Map<Object, GatewayGroup2apiConfig> method2apiConfigMap = path2methodToApiConfigMapMap.get(ac.path);
|
||||
if (method2apiConfigMap == null) {
|
||||
method2apiConfigMap = new HashMap<Object, GatewayGroup2apiConfig>();
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = new GatewayGroup2apiConfig();
|
||||
gatewayGroup2apiConfig.add(ac);
|
||||
method2apiConfigMap.put(ac.fizzMethod, gatewayGroup2apiConfig);
|
||||
path2methodToApiConfigMapMap.put(ac.path, method2apiConfigMap);
|
||||
} else {
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = method2apiConfigMap.get(ac.fizzMethod);
|
||||
if (gatewayGroup2apiConfig == null) {
|
||||
gatewayGroup2apiConfig = new GatewayGroup2apiConfig();
|
||||
method2apiConfigMap.put(ac.fizzMethod, gatewayGroup2apiConfig);
|
||||
gatewayGroup2apiConfig.add(ac);
|
||||
} else {
|
||||
log.info(id + " update " + ac);
|
||||
gatewayGroup2apiConfig.update(ac);
|
||||
ApiConfig prevApiConfig = null;
|
||||
for (String gatewayGroup : ac.gatewayGroups) {
|
||||
Map<Object, Map<String, ApiConfig>> method2pathPattenMap = apiConfigMap.get(gatewayGroup);
|
||||
if (method2pathPattenMap == null) {
|
||||
method2pathPattenMap = new HashMap<>();
|
||||
apiConfigMap.put(gatewayGroup, method2pathPattenMap);
|
||||
}
|
||||
Map<String, ApiConfig> pathPattern2apiConfigMap = method2pathPattenMap.get(ac.fizzMethod);
|
||||
if (pathPattern2apiConfigMap == null) {
|
||||
pathPattern2apiConfigMap = new HashMap<>();
|
||||
method2pathPattenMap.put(ac.fizzMethod, pathPattern2apiConfigMap);
|
||||
}
|
||||
prevApiConfig = pathPattern2apiConfigMap.put(ac.path, ac);
|
||||
}
|
||||
log.info("{} service update api config {} with {}", id, prevApiConfig, ac);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<ApiConfig> getApiConfigs(HttpMethod method, String path, String gatewayGroup) {
|
||||
|
||||
List<GatewayGroup2apiConfig> matchGatewayGroup2apiConfigs = ThreadContext.getArrayList(gg2acs);
|
||||
|
||||
Set<Map.Entry<String, Map<Object, GatewayGroup2apiConfig>>> es = path2methodToApiConfigMapMap.entrySet();
|
||||
for (Map.Entry<String, Map<Object, GatewayGroup2apiConfig>> e : es) {
|
||||
Map<Object, GatewayGroup2apiConfig> method2gatewayGroupToApiConfigMap = e.getValue();
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = method2gatewayGroupToApiConfigMap.get(method);
|
||||
if (gatewayGroup2apiConfig == null) {
|
||||
gatewayGroup2apiConfig = method2gatewayGroupToApiConfigMap.get(ApiConfig.ALL_METHOD);
|
||||
}
|
||||
if (gatewayGroup2apiConfig != null) {
|
||||
String pathPattern = e.getKey();
|
||||
if (ApiConfig.isAntPathPattern(pathPattern)) {
|
||||
if (UrlTransformUtils.ANT_PATH_MATCHER.match(pathPattern, path)) {
|
||||
matchGatewayGroup2apiConfigs.add(gatewayGroup2apiConfig);
|
||||
}
|
||||
} else if (path.equals(pathPattern)) {
|
||||
matchGatewayGroup2apiConfigs.add(gatewayGroup2apiConfig);
|
||||
}
|
||||
public List<ApiConfig> getApiConfigs(Set<String> gatewayGroups, HttpMethod method, String path) {
|
||||
ArrayList<ApiConfig> result = ThreadContext.getArrayList(gsmpT);
|
||||
for (String gatewayGroup : gatewayGroups) {
|
||||
List<ApiConfig> apiConfigs = getApiConfigs(gatewayGroup, method, path);
|
||||
result.addAll(apiConfigs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (matchGatewayGroup2apiConfigs.isEmpty()) {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, id + " no route match " + method + ' ' + path);
|
||||
@JsonIgnore
|
||||
public List<ApiConfig> getApiConfigs(String gatewayGroup, HttpMethod method, String path) {
|
||||
Map<Object, Map<String, ApiConfig>> method2pathPattenMap = apiConfigMap.get(gatewayGroup);
|
||||
if (method2pathPattenMap == null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
List<ApiConfig> lst = ThreadContext.getArrayList(acs);
|
||||
for (int i = 0; i < matchGatewayGroup2apiConfigs.size(); i++) {
|
||||
GatewayGroup2apiConfig gatewayGroup2apiConfig = matchGatewayGroup2apiConfigs.get(i);
|
||||
Set<ApiConfig> apiConfigs = gatewayGroup2apiConfig.get(gatewayGroup);
|
||||
if (apiConfigs == null) {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, "route which match " + id + ' ' + method + ' ' + path + " is not exposed to " + gatewayGroup);
|
||||
ArrayList<ApiConfig> result = ThreadContext.getArrayList(gmpT);
|
||||
Map<String, ApiConfig> pathPattern2apiConfigMap = method2pathPattenMap.get(method);
|
||||
if (pathPattern2apiConfigMap != null) {
|
||||
checkPathPattern(pathPattern2apiConfigMap, path, result);
|
||||
}
|
||||
pathPattern2apiConfigMap = method2pathPattenMap.get(ApiConfig.ALL_METHOD);
|
||||
if (pathPattern2apiConfigMap != null) {
|
||||
checkPathPattern(pathPattern2apiConfigMap, path, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPathPattern(Map<String, ApiConfig> pathPattern2apiConfigMap, String path, ArrayList<ApiConfig> result) {
|
||||
Set<Map.Entry<String, ApiConfig>> entries = pathPattern2apiConfigMap.entrySet();
|
||||
for (Map.Entry<String, ApiConfig> entry : entries) {
|
||||
String pathPattern = entry.getKey();
|
||||
ApiConfig apiConfig = entry.getValue();
|
||||
if (apiConfig.access == ApiConfig.ALLOW) {
|
||||
if (apiConfig.exactMatch) {
|
||||
if (pathPattern.equals(path)) {
|
||||
result.add(apiConfig);
|
||||
}
|
||||
} else {
|
||||
for (ApiConfig ac : apiConfigs) {
|
||||
if (ac.access == ApiConfig.ALLOW) {
|
||||
lst.add(ac);
|
||||
}
|
||||
}
|
||||
if (lst.isEmpty()) {
|
||||
ThreadContext.set(ApiConfigService.AUTH_MSG, "route which match " + id + ' ' + method + ' ' + path + " not allow access");
|
||||
if (UrlTransformUtils.ANT_PATH_MATCHER.match(pathPattern, path)) {
|
||||
result.add(apiConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,21 +24,20 @@ import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.plugin.PluginFilter;
|
||||
import we.plugin.auth.GatewayGroupService;
|
||||
import we.util.Consts;
|
||||
import we.util.ThreadContext;
|
||||
import we.util.WebUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
* @apiNote unstable.
|
||||
*/
|
||||
|
||||
@Component(StatPluginFilter.STAT_PLUGIN_FILTER)
|
||||
@@ -71,39 +70,35 @@ public class StatPluginFilter extends PluginFilter {
|
||||
@Resource
|
||||
private GatewayGroupService gatewayGroupService;
|
||||
|
||||
/*
|
||||
private String currentGatewayGroups;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Iterator<String> it = gatewayGroupService.currentGatewayGroupSet.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (StringUtils.isBlank(currentGatewayGroups)) {
|
||||
currentGatewayGroups = it.next();
|
||||
} else {
|
||||
currentGatewayGroups = currentGatewayGroups + ',' + it.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
|
||||
if (statPluginFilterProperties.isStatOpen()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
b.append(Consts.S.LEFT_BRACE);
|
||||
b.append(ip); toJsonStringValue(b, WebUtils.getOriginIp(exchange)); b.append(Consts.S.COMMA);
|
||||
b.append(gatewayGroup); toJsonStringValue(b, currentGatewayGroups()); b.append(Consts.S.COMMA);
|
||||
b.append(service); toJsonStringValue(b, WebUtils.getClientService(exchange)); b.append(Consts.S.COMMA);
|
||||
b.append(ip);
|
||||
toJsonStringValue(b, WebUtils.getOriginIp(exchange));
|
||||
b.append(Consts.S.COMMA);
|
||||
b.append(gatewayGroup);
|
||||
toJsonStringValue(b, currentGatewayGroups());
|
||||
b.append(Consts.S.COMMA);
|
||||
b.append(service);
|
||||
toJsonStringValue(b, WebUtils.getClientService(exchange));
|
||||
b.append(Consts.S.COMMA);
|
||||
|
||||
String appId = WebUtils.getAppId(exchange);
|
||||
if (appId != null) {
|
||||
b.append(appid); toJsonStringValue(b, appId); b.append(Consts.S.COMMA);
|
||||
b.append(appid);
|
||||
toJsonStringValue(b, appId);
|
||||
b.append(Consts.S.COMMA);
|
||||
}
|
||||
|
||||
b.append(apiMethod); toJsonStringValue(b, exchange.getRequest().getMethodValue()); b.append(Consts.S.COMMA);
|
||||
b.append(apiPath); toJsonStringValue(b, WebUtils.getClientReqPath(exchange)); b.append(Consts.S.COMMA);
|
||||
b.append(apiMethod);
|
||||
toJsonStringValue(b, exchange.getRequest().getMethodValue());
|
||||
b.append(Consts.S.COMMA);
|
||||
b.append(apiPath);
|
||||
toJsonStringValue(b, WebUtils.getClientReqPath(exchange));
|
||||
b.append(Consts.S.COMMA);
|
||||
b.append(reqTime).append(System.currentTimeMillis());
|
||||
b.append(Consts.S.RIGHT_BRACE);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import we.util.*;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -205,7 +206,10 @@ public class CallbackService {
|
||||
|
||||
public Mono<ReactiveResult> replay(CallbackReplayReq req) {
|
||||
|
||||
ApiConfig ac = apiConfigService.getApiConfig(req.service, req.method, req.path, req.gatewayGroup, req.app);
|
||||
HashSet<String> gatewayGroups = new HashSet<>();
|
||||
gatewayGroups.add(req.gatewayGroup);
|
||||
Result<ApiConfig> result = apiConfigService.getApiConfig(gatewayGroups, req.app, req.service, req.method, req.path);
|
||||
ApiConfig ac = result.data;
|
||||
if (ac == null) {
|
||||
return Mono.just(ReactiveResult.fail("no api config for " + req.path));
|
||||
}
|
||||
|
||||
@@ -174,12 +174,11 @@ public abstract class WebUtils {
|
||||
}
|
||||
|
||||
public static ApiConfig getApiConfig(ServerWebExchange exchange) {
|
||||
Object authRes = getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (authRes != null && authRes instanceof ApiConfig) {
|
||||
return (ApiConfig) authRes;
|
||||
} else {
|
||||
Result<ApiConfig> authRes = (Result<ApiConfig>) getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (authRes == null) {
|
||||
return null;
|
||||
}
|
||||
return authRes.data;
|
||||
}
|
||||
|
||||
public static Route getRoute(ServerWebExchange exchange) {
|
||||
|
||||
@@ -87,7 +87,7 @@ class CodecFuncTests {
|
||||
assertEquals("QmFzZTY057yW56CB5LuL57uN", result.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Test
|
||||
void testBase64Decode() {
|
||||
String funcExpression = "fn.codec.base64Decode(\"QmFzZTY057yW56CB5LuL57uN\")";
|
||||
Object result = FuncExecutor.getInstance().exec(null, funcExpression);
|
||||
|
||||
Reference in New Issue
Block a user