网关可校验token

This commit is contained in:
tanghc
2019-10-26 15:54:48 +08:00
parent 530a92a3ea
commit a08313ea81
33 changed files with 293 additions and 116 deletions

View File

@@ -32,12 +32,14 @@ import com.gitee.sop.gatewaycommon.validate.ApiSigner;
import com.gitee.sop.gatewaycommon.validate.ApiValidator;
import com.gitee.sop.gatewaycommon.validate.Encrypter;
import com.gitee.sop.gatewaycommon.validate.Signer;
import com.gitee.sop.gatewaycommon.validate.TokenValidator;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.configuration.ZuulErrorController;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder;
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
import com.netflix.zuul.context.RequestContext;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import java.util.ArrayList;
@@ -152,6 +154,11 @@ public class ApiConfig {
private ParameterFormatter parameterFormatter;
/**
* 校验token
*/
private TokenValidator tokenValidator = apiParam -> apiParam != null && StringUtils.isNotBlank(apiParam.fetchAccessToken());
// -------- fields ---------
/**

View File

@@ -1,46 +0,0 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Getter;
import lombok.Setter;
/**
* @author tanghc
*/
@Getter
@Setter
public class BaseRouteDefinition {
/**
* 路由的Id
*/
private String id;
/**
* 路由规则转发的目标uri
*/
private String uri;
/**
* uri后面跟的path
*/
private String path;
/**
* 路由执行的顺序
*/
private int order = 0;
/**
* 是否忽略验证,业务参数验证除外
*/
private int ignoreValidate;
/**
* 是否合并结果
*/
private int mergeResult;
/**
* 接口是否需要授权才能访问
*/
private int permission;
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
import java.util.Collections;
import java.util.List;
/**
* @author tanghc
*/
@Data
public class BaseServiceRouteInfo<T extends BaseRouteDefinition> {
private String serviceId;
private List<T> routeDefinitionList = Collections.emptyList();
public String fetchServiceIdLowerCase() {
return this.serviceId.toLowerCase();
}
}

View File

@@ -70,4 +70,9 @@ public class RouteDefinition {
* 是否需要授权才能访问
*/
private int permission;
/**
* 是否需要token
*/
private int needToken;
}

View File

@@ -16,6 +16,8 @@ import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -25,13 +27,19 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.Collections;
@@ -41,6 +49,7 @@ import java.util.List;
/**
* @author tanghc
*/
@Slf4j
public class BaseGatewayConfiguration extends AbstractConfiguration {
public BaseGatewayConfiguration() {
@@ -152,12 +161,20 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
@Bean
@ConditionalOnProperty(value = "sop.restful.enable", havingValue = "true")
RouterFunction<ServerResponse> routerFunction() {
return RouterFunctions.route(RequestPredicates.GET(restPath + "/**"), (serverRequest) -> {
String url = serverRequest.path();
int index = url.indexOf(restPath);
RequestPredicate requestPredicate = RequestPredicates.all()
.and(RequestPredicates.path(restPath + "/**"));
return RouterFunctions.route(requestPredicate, (serverRequest) -> {
String path = serverRequest.path();
int index = path.indexOf(restPath);
// 取/rest的后面部分
String path = url.substring(index + restPath.length());
String query = ParamNames.API_NAME + "=" + path + "&" + ParamNames.VERSION_NAME + "=";
String servletPath = path.substring(index + restPath.length());
String query = serverRequest.uri().getQuery();
String appendQuery = ParamNames.API_NAME + "=" + servletPath + "&" + ParamNames.VERSION_NAME + "=";
if (StringUtils.isBlank(query)) {
query = appendQuery;
} else {
query += '&' + appendQuery;
}
return ServerResponse
.temporaryRedirect(URI.create("/?" + query))
.build();

View File

@@ -21,7 +21,12 @@ public enum EnvironmentKeys {
/**
* sop.restful.enable=true开启传统web开发模式
*/
SOP_RESTFUL_ENABLE("sop.restful.enable");
SOP_RESTFUL_ENABLE("sop.restful.enable"),
/**
* sop.restful.path=/xx ,指定请求前缀,默认/rest
*/
SOP_RESTFUL_PATH("sop.restful.path", "/rest");
private String key;
private String defaultValue;

View File

@@ -83,6 +83,7 @@ public class ApiValidator implements Validator {
checkFormat(param);
checkUploadFile(param);
checkPermission(param);
checkToken(param);
}
/**
@@ -238,7 +239,7 @@ public class ApiValidator implements Validator {
/**
* 校验访问权限
*
* @param apiParam
* @param apiParam 参数
*/
protected void checkPermission(ApiParam apiParam) {
String routeId = apiParam.fetchNameVersion();
@@ -254,4 +255,23 @@ public class ApiValidator implements Validator {
}
}
/**
* 校验token
*
* @param apiParam 参数
*/
protected void checkToken(ApiParam apiParam) {
String routeId = apiParam.fetchNameVersion();
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId);
RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
boolean needToken = BooleanUtils.toBoolean(routeDefinition.getNeedToken());
if (needToken) {
TokenValidator tokenValidator = ApiConfig.getInstance().getTokenValidator();
boolean rightToken = tokenValidator.validateToken(apiParam);
if (!rightToken) {
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
}
}
}
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.gatewaycommon.validate;
import com.gitee.sop.gatewaycommon.param.ApiParam;
/**
* @author tanghc
*/
@FunctionalInterface
public interface TokenValidator {
boolean validateToken(ApiParam apiParam);
}

View File

@@ -35,4 +35,9 @@ public @interface ApiAbility {
* 指定接口是否需要授权才能访问可在admin中进行修改
*/
boolean permission() default false;
/**
* 是否需要appAuthToken设置为true网关端会校验token是否存在
*/
boolean needToken() default false;
}

View File

@@ -25,7 +25,7 @@ public @interface ApiMapping {
/**
* 版本号,默认版本号是""<br>
* 改默认版本号:<code>ServiceContext.getSopServerConfig().setDefaultVersion("1.0");</code>
* 改默认版本号:<code>ServiceConfig.getInstance().setDefaultVersion("1.0");</code>
*/
String version() default "";
@@ -44,6 +44,11 @@ public @interface ApiMapping {
*/
boolean permission() default false;
/**
* 是否需要appAuthToken设置为true网关端会校验token是否存在
*/
boolean needToken() default false;
// ------------ 自定义属性 end ------------

View File

@@ -80,12 +80,23 @@ public interface OpenContext<T> extends OpenBeanFactory {
*/
Date getTimestamp();
/**
* 返回token即access_token.
*
* @deprecated 废弃使用getAppAuthToken()
* @return 返回token
*/
@Deprecated
String appAuthToken();
/**
* 返回token即access_token
*
* @return 返回token
*/
String appAuthToken();
default String getAppAuthToken() {
return appAuthToken();
}
/**
* 返回回调地址

View File

@@ -28,6 +28,8 @@ public class ServiceApiInfo {
private int mergeResult;
/** 是否需要授权才能访问 */
private int permission;
/** 是否需要token */
private int needToken;
/** 是否是原始Mapping */
private boolean originalMapping;

View File

@@ -87,6 +87,7 @@ public class ApiMetaBuilder {
apiMeta.setIgnoreValidate(BooleanUtils.toInteger(apiMappingInfo.isIgnoreValidate()));
apiMeta.setMergeResult(BooleanUtils.toInteger(apiMappingInfo.isMergeResult()));
apiMeta.setPermission(BooleanUtils.toInteger(apiMappingInfo.isPermission()));
apiMeta.setNeedToken(BooleanUtils.toInteger(apiMappingInfo.isNeedToken()));
return apiMeta;
} else {
if (!ServiceContext.getCurrentContext().getBoolean(ServiceContext.RESTFUL_KEY, false)) {

View File

@@ -50,8 +50,8 @@ public class ServiceRouteInfoBuilder {
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis();
List<RouteDefinition> routeDefinitionList = new ArrayList<>(apis.size());
for (ServiceApiInfo.ApiMeta apiMeta : apis) {
RouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
routeDefinitionList.add(gatewayRouteDefinition);
RouteDefinition routeDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
routeDefinitionList.add(routeDefinition);
}
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId());

View File

@@ -35,9 +35,10 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
method.setAccessible(true);
String name = null;
String version;
boolean ignoreValidate = false;
boolean mergeResult = true;
boolean permission = false;
boolean ignoreValidate;
boolean mergeResult;
boolean permission;
boolean needToken = false;
ApiMapping apiMapping = method.getAnnotation(ApiMapping.class);
if (apiMapping != null) {
name = apiMapping.value()[0];
@@ -45,6 +46,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
ignoreValidate = apiMapping.ignoreValidate();
mergeResult = apiMapping.mergeResult();
permission = apiMapping.permission();
needToken = apiMapping.needToken();
} else {
ApiAbility apiAbility = this.findApiAbilityAnnotation(method);
if (apiAbility != null) {
@@ -52,6 +54,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
ignoreValidate = apiAbility.ignoreValidate();
mergeResult = apiAbility.mergeResult();
permission = apiAbility.permission();
needToken = apiAbility.needToken();
} else {
return super.getCustomMethodCondition(method);
}
@@ -75,6 +78,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
apiMappingInfo.setIgnoreValidate(ignoreValidate);
apiMappingInfo.setMergeResult(mergeResult);
apiMappingInfo.setPermission(permission);
apiMappingInfo.setNeedToken(needToken);
logger.info("注册接口name:" + method + " version:" + version);
return new ApiMappingRequestCondition(apiMappingInfo);
}

View File

@@ -12,6 +12,7 @@ public class ApiMappingInfo {
private boolean ignoreValidate;
private boolean mergeResult;
private boolean permission;
private boolean needToken;
public ApiMappingInfo(String name, String version) {
this.name = name;

View File

@@ -68,4 +68,9 @@ public class RouteDefinition {
* 是否需要授权才能访问
*/
private int permission;
/**
* 是否需要token
*/
private int needToken;
}