tanghc 6 years ago
parent 7d22c92122
commit f94009e2c4
  1. 5
      sop-book/sop-book-web/pom.xml
  2. 22
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java
  3. 40
      sop-book/sop-book-web/src/main/java/com/gitee/sop/bookweb/controller/AlipayBookController.java
  4. 6
      sop-book/sop-book-web/src/main/resources/application.properties
  5. 9
      sop-book/sop-book-web/src/main/resources/application.yml
  6. 7
      sop-book/sop-book-web/src/main/resources/bootstrap.yml
  7. 3
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java
  8. 38
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteRepository.java
  9. 4
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java
  10. 27
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LoadBalancerClientExtFilter.java
  11. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java
  12. 107
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java
  13. 9
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java
  14. 30
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java
  15. 8
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java
  16. 14
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorMeta.java
  17. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  18. 23
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RoutePathUtil.java
  19. 5
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  20. 41
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreTokenFilter.java
  21. 3
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java
  22. 10
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java
  23. 37
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java
  24. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulZookeeperRouteManager.java
  25. 11
      sop-gateway/pom.xml
  26. 4
      sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java
  27. 14
      sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java
  28. 45
      sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java
  29. 2
      sop-gateway/src/main/resources/application.yml
  30. 3
      sop-gateway/src/main/resources/bootstrap.yml
  31. 2
      sop-story/sop-story-web/pom.xml
  32. 7
      sop-story/sop-story-web/src/main/resources/application.properties
  33. 9
      sop-story/sop-story-web/src/main/resources/application.yml
  34. 80
      sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java
  35. 4
      sop-test/src/test/java/com/gitee/sop/TaobaoClientPostTest.java

@ -20,6 +20,11 @@
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-book-api</artifactId>

@ -0,0 +1,22 @@
package com.gitee.sop.bookweb.config;
import com.gitee.sop.servercommon.configuration.AlipayServiceConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* 使用支付宝开放平台功能
* @author tanghc
*/
@Configuration
public class OpenServiceConfig extends AlipayServiceConfiguration {
}
/**
* 使用淘宝开放平台功能
* @author tanghc
*/
//@Configuration
//public class OpenServiceConfig extends TaobaoServiceConfiguration {
//
//}

@ -0,0 +1,40 @@
package com.gitee.sop.bookweb.controller;
import com.gitee.sop.bookweb.consumer.StoryServiceConsumer;
import com.gitee.sop.servercommon.annotation.ApiMapping;
import com.gitee.sop.story.api.domain.Story;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
/**
* 支付宝服务端假设签名验证通过后到达这里进行具体的业务处理
* 这里演示如何接受业务参数
* @author tanghc
*/
@RestController
public class AlipayBookController {
@Autowired
StoryServiceConsumer storyServiceConsumer;
@ApiMapping(value = "alipay.book.get")
public Story getBook() {
Story story = new Story();
story.setId(1);
story.setName("白雪公主(alipay.book.get)");
return story;
}
// 调用story服务
@ApiMapping(value = "alipay.book.story.get")
public Object getBook2() {
Story story = new Story();
story.setId(1);
story.setName("白雪公主(alipay.book.story.get)");
Story story2 = storyServiceConsumer.getStory(1);
return Arrays.asList(story, story2);
}
}

@ -1,6 +0,0 @@
server.port=3333
spring.application.name=book-service
eureka.host=localhost
eureka.port=1111
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/

@ -0,0 +1,9 @@
server:
port: 3333
eureka:
port: 1111
host: localhost
client:
serviceUrl:
defaultZone: http://${eureka.host}:${eureka.port}/eureka/

@ -0,0 +1,7 @@
spring:
application:
name: book-service
cloud:
zookeeper:
connect-string: localhost:2181

@ -4,6 +4,9 @@ import lombok.Data;
import java.util.List;
/**
* @author thc
*/
@Data
public class BaseServiceRouteInfo<T extends BaseRouteDefinition> {
private String appName;

@ -0,0 +1,38 @@
package com.gitee.sop.gatewaycommon.bean;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* @author tanghc
*/
public abstract class ServiceRouteRepository<R, E> {
/**
* key:serviceId
*/
private Map<String, Set<E>> serviceRouteMap = new ConcurrentHashMap<>();
public abstract String getServiceId(R r);
public void saveRouteDefinition(R serviceRouteInfo, E definition) {
String serverId = getServiceId(serviceRouteInfo);
Set<E> routeDefinitionSet = serviceRouteMap.putIfAbsent(serverId, new HashSet<>(16));
if (routeDefinitionSet == null) {
routeDefinitionSet = serviceRouteMap.get(serverId);
}
routeDefinitionSet.add(definition);
}
public synchronized void deleteAll(R serviceRouteInfo, Consumer<E> consumer) {
String serverId = getServiceId(serviceRouteInfo);
Set<E> definitionSet = serviceRouteMap.getOrDefault(serverId, Collections.emptySet());
for (E routeDefinition : definitionSet) {
consumer.accept(routeDefinition);
}
definitionSet.clear();
}
}

@ -26,6 +26,10 @@ import reactor.core.publisher.Mono;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
/**
* 修改返回结果
* @author thc
*/
public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered {

@ -1,5 +1,6 @@
package com.gitee.sop.gatewaycommon.gateway.filter;
import com.gitee.sop.gatewaycommon.util.RoutePathUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
@ -15,10 +16,9 @@ import java.net.URI;
import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
/**
* 在LoadBalancerClientFilter后面处理处理成我们想要的uri
* 在LoadBalancerClientFilter后面处理从Route中找到具体的path然后插入到uri的path中
*
* @author tanghc
*/
@ -32,25 +32,22 @@ public class LoadBalancerClientExtFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
URI routeUri = route.getUri();
URI requestUrl = url;
String uriStr = routeUri.toString();
String[] uriArr = uriStr.split("\\#");
if (uriArr.length == 2) {
String path = uriArr[1];
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(url);
String path = this.findPath(route);
if (StringUtils.hasLength(path)) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(url);
uriComponentsBuilder.path(path);
URI requestUrl = uriComponentsBuilder.build(true).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
}
requestUrl = uriComponentsBuilder.build(true).toUri();
return chain.filter(exchange);
}
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
protected String findPath(Route route) {
URI routeUri = route.getUri();
String uriStr = routeUri.toString();
return RoutePathUtil.findPath(uriStr);
}
}

@ -39,7 +39,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
* 错误记录
*/
ServerHttpRequest request = exchange.getRequest();
log.error("[全局异常处理]异常请求路径:{},记录异常信息:{}", request.getPath(), ex.getMessage());
log.error("[全局异常处理]异常请求路径:{}, msg:{}", request.getPath(), ex.getMessage(), ex);
/**
* 参考AbstractErrorWebExceptionHandler
*/

@ -1,74 +1,123 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.PredicateArgsEvent;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ConfigurationUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.Validator;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 动态更新路由
* 路由存储管理负责动态更新路由
*
* @author thc
*/
@Slf4j
public class GatewayRouteRepository implements ApplicationEventPublisherAware, RouteRepository<RouteDefinition> {
public class GatewayRouteRepository extends InMemoryRouteDefinitionRepository
implements ApplicationEventPublisherAware,
BeanFactoryAware,
RouteRepository<GatewayServiceRouteInfo, RouteDefinition> {
private final SpelExpressionParser parser = new SpelExpressionParser();
private ServiceRouteRepository<GatewayServiceRouteInfo, RouteDefinition> serviceRouteRepository = new ServiceRouteRepository<GatewayServiceRouteInfo, RouteDefinition>() {
@Override
public String getServiceId(GatewayServiceRouteInfo serviceRouteInfo) {
return serviceRouteInfo.getAppName();
}
};
@Autowired
private ConversionService conversionService;
@Autowired
private RouteDefinitionRepository routeDefinitionRepository;
private Validator validator;
private ApplicationEventPublisher publisher;
/** 根据ID获取路由 */
private BeanFactory beanFactory;
/**
* 根据ID获取路由
*/
@Override
public RouteDefinition get(String id) {
return routeDefinitionRepository.getRouteDefinitions()
return getRouteDefinitions()
.filter(routeDefinition -> {
return routeDefinition.getId().equals(id);
}).blockFirst();
}
/** 增加路由 */
/**
* 增加路由
*/
@Override
public String add(RouteDefinition definition) {
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
public String add(GatewayServiceRouteInfo serviceRouteInfo, RouteDefinition definition) {
super.save(Mono.just(definition)).subscribe();
serviceRouteRepository.saveRouteDefinition(serviceRouteInfo, definition);
this.initPredicateDefinition(definition);
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
/** 更新路由 */
@Override
public String update(RouteDefinition definition) {
log.info("更新route,id:{}", definition.getId());
try {
this.routeDefinitionRepository.delete(Mono.just(definition.getId()));
} catch (Exception e) {
return "update fail,not find route routeId: " + definition.getId();
protected void initPredicateDefinition(RouteDefinition definition) {
for (PredicateDefinition predicate : definition.getPredicates()) {
Map<String, String> args = predicate.getArgs();
if (!args.isEmpty()) {
RoutePredicateFactory<NameVersionRoutePredicateFactory.Config> factory = new NameVersionRoutePredicateFactory();
Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
Object config = factory.newConfig();
ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(),
validator, conversionService);
this.publisher.publishEvent(new PredicateArgsEvent(this, definition.getId(), properties));
}
try {
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception e) {
return "update route fail";
}
}
/** 删除路由 */
/**
* 删除路由
*/
@Override
public void delete(String id) {
this.routeDefinitionRepository.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
super.delete(Mono.just(id));
this.publisher.publishEvent(new PredicateArgsEvent(this, id, Collections.emptyMap()));
}
@Override
public void deleteAll(GatewayServiceRouteInfo serviceRouteInfo) {
serviceRouteRepository.deleteAll(serviceRouteInfo, routeDefinition -> {
this.delete(routeDefinition.getId());
});
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

@ -21,9 +21,9 @@ import java.util.List;
*/
@Getter
@Slf4j
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayRouteDefinition, GatewayServiceRouteInfo, RouteDefinition> {
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayServiceRouteInfo, GatewayRouteDefinition, RouteDefinition> {
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<RouteDefinition> routeRepository) {
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<GatewayServiceRouteInfo, RouteDefinition> routeRepository) {
super(environment, routeRepository);
}
@ -32,11 +32,6 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayRouteD
return GatewayServiceRouteInfo.class;
}
@Override
protected Class<GatewayRouteDefinition> getRouteDefinitionClass() {
return GatewayRouteDefinition.class;
}
@Override
protected RouteDefinition buildRouteDefinition(GatewayServiceRouteInfo serviceRouteInfo,GatewayRouteDefinition gatewayRouteDefinition) {
RouteDefinition routeDefinition = new RouteDefinition();

@ -18,25 +18,29 @@ import java.util.List;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_API_PATH;
/**
* 路由管理
*
* @param <R> 路由根对象可以理解为最外面的大json{....,routeDefinitionList:[]}
* @param <E> 路由Item对象对应大json里面的具体路由信息routeDefinitionList:[]
* @param <T> 目标路由对象返回最终的路由对象
* @author tanghc
*/
@Slf4j
public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends BaseServiceRouteInfo<E>, T> implements RouteManager {
public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E extends BaseRouteDefinition, T> implements RouteManager {
protected String sopServiceApiPath = SOP_SERVICE_API_PATH;
protected Environment environment;
protected RouteRepository<T> routeRepository;
protected RouteRepository<R, T> routeRepository;
protected abstract Class<R> getServiceRouteInfoClass();
protected abstract Class<E> getRouteDefinitionClass();
protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition);
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
public BaseRouteManager(Environment environment, RouteRepository<R, T> routeRepository) {
this.environment = environment;
this.routeRepository = routeRepository;
}
@ -91,7 +95,7 @@ public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends B
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.add(routeDefinition);
routeRepository.add(serviceRouteInfo, routeDefinition);
}
}
// 添加事件监听器
@ -99,6 +103,7 @@ public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends B
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
PathChildrenCacheEvent.Type type = event.getType();
synchronized (type) {
// 通过判断event type的方式来实现不同事件的触发
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
String nodeData = new String(event.getData().getData());
@ -107,24 +112,25 @@ public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends B
log.info("子节点:{}添加,数据为:{}", event.getData().getPath(), nodeData);
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.add(routeDefinition);
routeRepository.add(serviceRouteInfo, routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 修改子节点数据时触发
log.info("子节点:{}修改,数据为:{}", event.getData().getPath(), nodeData);
// 删除下面所有节点
routeRepository.deleteAll(serviceRouteInfo);
// 添加新节点
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.update(routeDefinition);
routeRepository.add(serviceRouteInfo, routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 删除子节点时触发
log.info("子节点:{}删除,数据为:{}", event.getData().getPath(), nodeData);
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
routeRepository.delete(routeDefinitionItem.getId());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
routeRepository.deleteAll(serviceRouteInfo);
}
}
}

@ -3,12 +3,12 @@ package com.gitee.sop.gatewaycommon.manager;
/**
* @author tanghc
*/
public interface RouteRepository<T> {
public interface RouteRepository<R, T> {
T get(String id);
String add(T route);
String update(T route);
String add(R serviceRouteInfo, T route);
void delete(String id);
void deleteAll(R serviceRouteInfo);
}

@ -2,8 +2,11 @@ package com.gitee.sop.gatewaycommon.message;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.netflix.zuul.context.RequestContext;
import lombok.Getter;
import sun.plugin.cache.CacheUpdateHelper;
import javax.servlet.http.HttpServletRequest;
import java.util.Locale;
/**
@ -37,7 +40,7 @@ public class ErrorMeta {
* @return 返回exception
*/
public ApiException getException(Object... params) {
Locale locale = ZH_CN;
Locale locale = getLocale();
if (params != null && params.length == 1) {
Object param = params[0];
if (param instanceof Throwable) {
@ -52,4 +55,13 @@ public class ErrorMeta {
return new ApiException(error);
}
protected Locale getLocale() {
RequestContext currentContext = RequestContext.getCurrentContext();
if (currentContext == null) {
return ZH_CN;
}
HttpServletRequest request = currentContext.getRequest();
return request == null ? ZH_CN : request.getLocale();
}
}

@ -7,6 +7,7 @@ import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import java.util.Map;
@ -23,6 +24,10 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
public static final String GATEWAY_CODE_NAME = "code";
public static final String GATEWAY_MSG_NAME = "msg";
public static final String DATA_SUFFIX = "_response";
public static final String ARRAY_START = "[";
public static final String ARRAY_END = "]";
public static final String ROOT_JSON = "{'items':%s}".replace("'", "\"");
/**
* 获取业务方约定的返回码
@ -40,6 +45,7 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
@Override
public String mergeResult(T request, String serviceResult) {
serviceResult = wrapResult(serviceResult);
int responseStatus = this.getBizHeaderCode(request);
JSONObject jsonObjectService;
if (responseStatus == HttpStatus.OK.value()) {
@ -62,6 +68,20 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
return this.merge(request, jsonObjectService);
}
protected String wrapResult(String serviceResult) {
if (serviceResult == null) {
serviceResult = "";
}
serviceResult = serviceResult.trim();
if (StringUtils.isEmpty(serviceResult)) {
return SopConstants.EMPTY_JSON;
}
if (serviceResult.startsWith(ARRAY_START) && serviceResult.endsWith(ARRAY_END)) {
return String.format(ROOT_JSON, serviceResult);
}
return serviceResult;
}
public String merge(T exchange, JSONObject jsonObjectService) {
JSONObject ret = new JSONObject();
// 点换成下划线

@ -0,0 +1,23 @@
package com.gitee.sop.gatewaycommon.util;
import org.springframework.cloud.gateway.route.Route;
import java.net.URI;
/**
* @author tanghc
*/
public class RoutePathUtil {
public static final String REGEX = "\\#";
public static String findPath(String uri) {
// #后面是对应的path
String[] uriArr = uri.split(REGEX);
if (uriArr.length == 2) {
return uriArr[1];
} else {
return null;
}
}
}

@ -36,6 +36,11 @@ public class BaseZuulConfiguration {
@Autowired
protected RouteManager apiMetaManager;
@Bean
SopRouteLocator sopRouteLocator(ZuulRouteRepository zuulRouteRepository) {
return new SopRouteLocator(zuulRouteRepository);
}
@Bean
ZuulZookeeperRouteManager zuulZookeeperRouteManager(Environment environment, ZuulRouteRepository zuulRouteRepository) {
return new ZuulZookeeperRouteManager(environment, zuulRouteRepository);

@ -1,41 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* @author tanghc
*/
public class PreTokenFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
protected Object doRun(RequestContext ctx) throws ZuulException {
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY);
log.info("serviceId:{}", serviceId);
HttpServletRequest request = ctx.getRequest();
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
String accessToken = request.getParameter("access_token");
if (StringUtils.isBlank(accessToken)) {
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
}
return null;
}
}

@ -7,6 +7,7 @@ import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.message.Error;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
@ -34,7 +35,7 @@ public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, Stri
@Override
public Map<String, ?> getApiParam(RequestContext requestContext) {
return (Map<String, ?>) requestContext.get(SopConstants.CACHE_API_PARAM);
return ZuulContext.getApiParam();
}
@Override

@ -16,10 +16,10 @@ import java.util.List;
*/
public class SopRouteLocator implements RouteLocator, Ordered {
private RouteRepository<Route> routeRepository;
private ZuulRouteRepository zuulRouteRepository;
public SopRouteLocator(RouteRepository<Route> routeRepository) {
this.routeRepository = routeRepository;
public SopRouteLocator(ZuulRouteRepository zuulRouteRepository) {
this.zuulRouteRepository = zuulRouteRepository;
}
@Override
@ -29,14 +29,14 @@ public class SopRouteLocator implements RouteLocator, Ordered {
@Override
public List<Route> getRoutes() {
return null;
return zuulRouteRepository.listAll();
}
@Override
public Route getMatchingRoute(String path) {
ApiParam param = ZuulContext.getApiParam();
String nameVersion = param.fetchNameVersion();
return routeRepository.get(nameVersion);
return zuulRouteRepository.get(nameVersion);
}
@Override

@ -1,18 +1,34 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import org.springframework.cloud.netflix.zuul.filters.Route;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author tanghc
*/
public class ZuulRouteRepository implements RouteRepository<Route> {
/** key:nameVersion */
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128);
public class ZuulRouteRepository implements RouteRepository<ZuulServiceRouteInfo, Route> {
/**
* keynameVersion
*/
private Map<String, Route> nameVersionServiceIdMap = new ConcurrentHashMap<>(128);
private ServiceRouteRepository<ZuulServiceRouteInfo, Route> serviceRouteRepository = new ServiceRouteRepository<ZuulServiceRouteInfo, Route>() {
@Override
public String getServiceId(ZuulServiceRouteInfo serviceRouteInfo) {
return serviceRouteInfo.getAppName();
}
};
public List<Route> listAll() {
return new ArrayList<>(nameVersionServiceIdMap.values());
}
@Override
public Route get(String id) {
@ -24,14 +40,17 @@ public class ZuulRouteRepository implements RouteRepository<Route> {
}
@Override
public String add(Route route) {
return this.update(route);
public String add(ZuulServiceRouteInfo serviceRouteInfo, Route route) {
nameVersionServiceIdMap.put(route.getId(), route);
serviceRouteRepository.saveRouteDefinition(serviceRouteInfo, route);
return null;
}
@Override
public String update(Route route) {
nameVersionServiceIdMap.put(route.getId(), route);
return null;
public void deleteAll(ZuulServiceRouteInfo serviceRouteInfo) {
serviceRouteRepository.deleteAll(serviceRouteInfo, route -> {
this.delete(route.getId());
});
}
@Override

@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.manager.BaseRouteManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.util.RoutePathUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.core.env.Environment;
@ -10,9 +11,9 @@ import org.springframework.core.env.Environment;
* @author tanghc
*/
@Slf4j
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulRouteDefinition, ZuulServiceRouteInfo, Route> {
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulServiceRouteInfo, ZuulRouteDefinition, Route> {
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<Route> routeRepository) {
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<ZuulServiceRouteInfo, Route> routeRepository) {
super(environment, routeRepository);
}
@ -21,13 +22,8 @@ public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulRouteDefinit
return ZuulServiceRouteInfo.class;
}
@Override
protected Class<ZuulRouteDefinition> getRouteDefinitionClass() {
return ZuulRouteDefinition.class;
}
@Override
protected Route buildRouteDefinition(ZuulServiceRouteInfo serviceRouteInfo, ZuulRouteDefinition routeDefinition) {
return new Route(routeDefinition.getId(), routeDefinition.getPath(), serviceRouteInfo.getAppName(), null, false, null);
return new Route(routeDefinition.getId(), RoutePathUtil.findPath(routeDefinition.getUri()), serviceRouteInfo.getAppName(), null, false, null);
}
}

@ -26,19 +26,15 @@
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓
如果要使用gateway需要注释掉
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
-->
<!-- ↑↑↑ 使用spring cloud zuul ↑↑↑ -->
<!-- ↓↓↓ 使用spring cloud gateway ↓↓↓
如果要使用zuul需要注释掉
-->
<!-- ↓↓↓ 使用spring cloud gateway,处于beta阶段,推荐使用zuul ↓↓↓
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
@ -47,6 +43,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
-->
<!-- ↑↑↑ 使用spring cloud gateway ↑↑↑ -->
<dependency>

@ -2,10 +2,10 @@ package com.gitee.sop.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
// 开启网关功能
//@EnableZuulProxy
@EnableZuulProxy
@SpringBootApplication
public class SopGatewayApplication {

@ -2,16 +2,27 @@ package com.gitee.sop.gateway.config;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 使用Spring Cloud Gateway
*
* 注意下面两个只能使用一个
*
* 使用前前往启动类SopGatewayApplication.java 注释掉@EnableZuulProxy
*/
/**
* 开通支付宝开放平台能力
* @author tanghc
*/
@Configuration
//@Configuration
public class GatewayConfig extends AlipayGatewayConfiguration {
{
@ -33,3 +44,4 @@ public class GatewayConfig extends AlipayGatewayConfiguration {
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
// }
//}

@ -0,0 +1,45 @@
package com.gitee.sop.gateway.config;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
import com.gitee.sop.gatewaycommon.zuul.configuration.TaobaoZuulConfiguration;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 使用Spring Cloud Zuul推荐使用
*
* 注意下面两个只能使用一个
*/
/**
* 开通支付宝开放平台能力
* @author tanghc
*/
@Configuration
public class ZuulConfig extends AlipayZuulConfiguration {
{
Map<String, String> appSecretStore = new HashMap();
appSecretStore.put("alipay_test", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
ApiContext.getApiConfig().addAppSecret(appSecretStore);
}
}
/**
* 开通支付宝开放平台能力
* @author tanghc
*/
//@Configuration
//public class ZuulConfig extends TaobaoZuulConfiguration {
//
// {
// Map<String, String> appSecretStore = new HashMap();
// appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
// }
//}

@ -1,6 +1,7 @@
server:
port: 8081
eureka:
serverAddr: localhost:1111
client:
@ -14,3 +15,4 @@ spring:
locator:
lower-case-service-id: true
enabled: true

@ -6,3 +6,6 @@ spring:
cloud:
zookeeper:
connect-string: localhost:2181
zuul:
servlet-path: /api # 入口地址,默认是/zuul

@ -22,7 +22,7 @@
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-server-common</artifactId>
<artifactId>sop-service-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>

@ -1,7 +0,0 @@
server.port=2222
spring.application.name=story-service
eureka.host=localhost
eureka.port=1111
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/

@ -0,0 +1,9 @@
server:
port: 2222
eureka:
port: 1111
host: localhost
client:
serviceUrl:
defaultZone: http://${eureka.host}:${eureka.port}/eureka/

@ -22,7 +22,7 @@ import java.util.Set;
public class AlipayClientPostTest extends TestBase {
String url = "http://localhost:8081/";
String url = "http://localhost:8081/api";
String appId = "alipay_test";
// 支付宝私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
@ -40,6 +40,7 @@ version String 是 3 调用的接口版本,固定为:1.0 1.0
app_auth_token String 40 详见应用授权概述
biz_content String 请求参数的集合最大长度不限除公共参数外所有请求参数都必须放在这个参数中传递具体参照各产品快速接入文档
*/
// 这个请求会路由到story服务
@Test
public void testPost() throws Exception {
@ -51,7 +52,82 @@ biz_content String 是 请求参数的集合,最大长度不限,除公共
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.2");
params.put("version", "1.0");
// 业务参数
Map<String, String> bizContent = new HashMap<>();
bizContent.put("id", "1");
bizContent.put("name", "葫芦娃");
// bizContent.put("name", "葫芦娃1234567890葫芦娃1234567890"); // 超出长度
params.put("biz_content", JSON.toJSONString(bizContent));
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("----------- 返回结果 -----------");
String responseData = post(url, params);// 发送请求
System.out.println(responseData);
}
// 这个请求会路由到book服务
@Test
public void testPostBook() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "alipay.book.get");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, String> bizContent = new HashMap<>();
bizContent.put("id", "1");
bizContent.put("name", "葫芦娃");
// bizContent.put("name", "葫芦娃1234567890葫芦娃1234567890"); // 超出长度
params.put("biz_content", JSON.toJSONString(bizContent));
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("----------- 返回结果 -----------");
String responseData = post(url, params);// 发送请求
System.out.println(responseData);
}
// 这个请求会路由到book服务,然后再调用story服务
// gateway -> book-service -> story-service
@Test
public void testPostBook2() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "alipay.book.story.get");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, String> bizContent = new HashMap<>();

@ -15,9 +15,9 @@ import java.util.Map;
public class TaobaoClientPostTest extends TestBase {
String url = "http://localhost:8081";
String url = "http://localhost:8081/api";
String appId = "taobao_test";
// 支付宝私钥
// 宝私钥
String secret = "G9w0BAQEFAAOCAQ8AMIIBCgKCA";
/*

Loading…
Cancel
Save