初始化提交
This commit is contained in:
16
gateway/gateway-admin/.gitignore
vendored
Normal file
16
gateway/gateway-admin/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
target/
|
||||
logs/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
33
gateway/gateway-admin/pom.xml
Normal file
33
gateway/gateway-admin/pom.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>gateway-admin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>gateway-admin</name>
|
||||
<description>Demo Gateway Admin project for Spring Cloud</description>
|
||||
|
||||
<parent>
|
||||
<artifactId>webapp-parent</artifactId>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../webapps/webapp-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-starter-redis</artifactId>
|
||||
<version>2.5.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-gateway-core</artifactId>
|
||||
<version>2.1.0.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
66
gateway/gateway-admin/src/main/db/db.sql
Normal file
66
gateway/gateway-admin/src/main/db/db.sql
Normal file
@ -0,0 +1,66 @@
|
||||
SET NAMES utf8;
|
||||
|
||||
DROP DATABASE IF EXISTS sc_gateway;
|
||||
CREATE DATABASE sc_gateway DEFAULT CHARSET utf8mb4;
|
||||
|
||||
USE sc_gateway;
|
||||
|
||||
-- 网关路由表
|
||||
DROP TABLE IF EXISTS gateway_route;
|
||||
CREATE TABLE gateway_route
|
||||
(
|
||||
id VARCHAR(20) PRIMARY KEY COMMENT 'id',
|
||||
route_id VARCHAR(100) NOT NULL COMMENT '路由id',
|
||||
uri VARCHAR(100) NOT NULL COMMENT 'uri路径',
|
||||
predicates TEXT NOT NULL COMMENT '判定器',
|
||||
filters TEXT COMMENT '过滤器',
|
||||
orders INT COMMENT '排序',
|
||||
description VARCHAR(500) COMMENT '描述',
|
||||
status VARCHAR(1) DEFAULT 'Y' COMMENT '状态:Y-有效,N-无效',
|
||||
created_time DATETIME NOT NULL DEFAULT now() COMMENT '创建时间',
|
||||
updated_time DATETIME NOT NULL DEFAULT now() COMMENT '更新时间',
|
||||
created_by VARCHAR(100) NOT NULL COMMENT '创建人',
|
||||
updated_by VARCHAR(100) NOT NULL COMMENT '更新人'
|
||||
) COMMENT '网关路由表';
|
||||
|
||||
CREATE UNIQUE INDEX ux_gateway_routes_uri ON gateway_route (uri);
|
||||
|
||||
|
||||
-- DML初始数据
|
||||
-- 路由数据
|
||||
INSERT INTO gateway_route (id, route_id, uri, predicates, filters, orders, description, status, created_time, updated_time, created_by, updated_by)
|
||||
VALUES
|
||||
(101,
|
||||
'authorization-server',
|
||||
'lb://authorization-server:8000',
|
||||
'[{"name":"Path","args":{"pattern":"/authorization-server/**"}}]',
|
||||
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||
100,
|
||||
'授权认证服务网关注册',
|
||||
'Y', now(), now(), 'system', 'system'),
|
||||
(102,
|
||||
'authentication-server',
|
||||
'lb://authentication-server:8001',
|
||||
'[{"name":"Path","args":{"pattern":"/authentication-server/**"}}]',
|
||||
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||
100,
|
||||
'签权服务网关注册',
|
||||
'Y', now(), now(), 'system', 'system'),
|
||||
(103,
|
||||
'organization',
|
||||
'lb://organization:8010',
|
||||
'[{"name":"Path","args":{"pattern":"/organization/**"}}]',
|
||||
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||
100,
|
||||
'系统管理相关接口',
|
||||
'Y', now(), now(), 'system', 'system'),
|
||||
(104,
|
||||
'gateway-admin',
|
||||
'lb://gateway-admin:8445',
|
||||
'[{"name":"Path","args":{"pattern":"/gateway-admin/**"}}]',
|
||||
'[{"name":"StripPrefix","args":{"parts":"1"}}]',
|
||||
100,
|
||||
'网关管理相关接口',
|
||||
'Y', now(), now(), 'system', 'system')
|
||||
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.springboot.cloud.gateway.admin;
|
||||
|
||||
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
|
||||
import com.alicp.jetcache.anno.config.EnableMethodCache;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
|
||||
|
||||
@SpringBootApplication(exclude = GatewayClassPathWarningAutoConfiguration.class)
|
||||
@EnableDiscoveryClient
|
||||
@EnableCircuitBreaker
|
||||
@EnableMethodCache(basePackages = "com.springboot.cloud")
|
||||
@EnableCreateCacheAnnotation
|
||||
public class GatewayAdminApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayAdminApplication.class, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class BusConfig {
|
||||
|
||||
public static final String QUEUE_NAME = "event-gateway";
|
||||
public static final String EXCHANGE_NAME = "spring-boot-exchange";
|
||||
public static final String ROUTING_KEY = "gateway-route";
|
||||
|
||||
@Bean
|
||||
Queue queue() {
|
||||
log.info("queue name:{}", QUEUE_NAME);
|
||||
return new Queue(QUEUE_NAME, false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
TopicExchange exchange() {
|
||||
log.info("exchange:{}", EXCHANGE_NAME);
|
||||
return new TopicExchange(EXCHANGE_NAME);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Binding binding(Queue queue, TopicExchange exchange) {
|
||||
log.info("binding {} to {} with {}", queue, exchange, ROUTING_KEY);
|
||||
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageConverter messageConverter() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import com.springboot.cloud.common.web.handler.PoMetaObjectHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MyMetaObjectHandler extends PoMetaObjectHandler {
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import com.springboot.cloud.common.web.redis.RedisConfig;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class MyRedisConfig extends RedisConfig {
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 初使化Mybatis审计字段自动赋值的interceptor
|
||||
*/
|
||||
@EnableTransactionManagement
|
||||
@Configuration
|
||||
public class MybatisConfig {
|
||||
/**
|
||||
* 分页插件
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
return new PaginationInterceptor();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfig {
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.springboot.cloud.gateway.admin"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("网关管理api")
|
||||
.description("网关管理")
|
||||
.termsOfServiceUrl("https://github.com/zhoutaoo/SpringCloud")
|
||||
.version("2.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.springboot.cloud.gateway.admin.config;
|
||||
|
||||
import com.springboot.cloud.common.web.interceptor.UserInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebServerMvcConfigurerAdapter implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
public HandlerInterceptor userInterceptor() {
|
||||
return new UserInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(userInterceptor());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.springboot.cloud.gateway.admin.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface GatewayRouteMapper extends BaseMapper<GatewayRoute> {
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.form;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.springboot.cloud.common.web.entity.form.BaseForm;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.FilterDefinition;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.PredicateDefinition;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ApiModel
|
||||
@Data
|
||||
@Slf4j
|
||||
public class GatewayRouteForm extends BaseForm<GatewayRoute> {
|
||||
|
||||
@NotEmpty(message = "网关断言不能为空")
|
||||
@ApiModelProperty(value = "网关断言")
|
||||
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||
|
||||
@ApiModelProperty(value = "网关过滤器信息")
|
||||
private List<FilterDefinition> filters = new ArrayList<>();
|
||||
|
||||
@NotBlank(message = "uri不能为空")
|
||||
@ApiModelProperty(value = "网关uri")
|
||||
private String uri;
|
||||
|
||||
@NotBlank(message = "路由id不能为空")
|
||||
@ApiModelProperty(value = "网关路由id")
|
||||
private String routeId;
|
||||
|
||||
@ApiModelProperty(value = "排序")
|
||||
private Integer orders = 0;
|
||||
|
||||
@ApiModelProperty(value = "网关路由描述信息")
|
||||
private String description;
|
||||
|
||||
@Override
|
||||
public GatewayRoute toPo(Class<GatewayRoute> clazz) {
|
||||
GatewayRoute gatewayRoute = new GatewayRoute();
|
||||
BeanUtils.copyProperties(this, gatewayRoute);
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
gatewayRoute.setFilters(objectMapper.writeValueAsString(this.getFilters()));
|
||||
gatewayRoute.setPredicates(objectMapper.writeValueAsString(this.getPredicates()));
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("网关filter或predicates配置转换异常", e);
|
||||
}
|
||||
return gatewayRoute;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.form;
|
||||
|
||||
import com.springboot.cloud.common.web.entity.form.BaseQueryForm;
|
||||
import com.springboot.cloud.gateway.admin.entity.param.GatewayRouteQueryParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.Past;
|
||||
import java.util.Date;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ApiModel
|
||||
@Data
|
||||
public class GatewayRouteQueryForm extends BaseQueryForm<GatewayRouteQueryParam> {
|
||||
|
||||
@ApiModelProperty(value = "uri路径", required = true)
|
||||
private String uri;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
@Past(message = "查询开始时间必须小于当前日期")
|
||||
@ApiModelProperty(value = "查询开始时间")
|
||||
private Date createdTimeStart;
|
||||
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
@Past(message = "查询结束时间必须小于当前日期")
|
||||
@ApiModelProperty(value = "查询结束时间")
|
||||
private Date createdTimeEnd;
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.ov;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.springboot.cloud.common.web.entity.vo.BaseVo;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.FilterDefinition;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.PredicateDefinition;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Slf4j
|
||||
public class GatewayRouteVo extends BaseVo {
|
||||
private String id;
|
||||
private String routeId;
|
||||
private String description;
|
||||
private String status;
|
||||
private String uri;
|
||||
private Integer orders;
|
||||
private String createdBy;
|
||||
private Date createdTime;
|
||||
private String updatedBy;
|
||||
private Date updatedTime;
|
||||
private List<FilterDefinition> filters = new ArrayList<>();
|
||||
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||
|
||||
public GatewayRouteVo(GatewayRoute gatewayRoute) {
|
||||
this.id = gatewayRoute.getId();
|
||||
this.routeId = gatewayRoute.getRouteId();
|
||||
this.uri = gatewayRoute.getUri();
|
||||
this.description = gatewayRoute.getDescription();
|
||||
this.status = gatewayRoute.getStatus();
|
||||
this.orders = gatewayRoute.getOrders();
|
||||
this.createdBy = gatewayRoute.getCreatedBy();
|
||||
this.createdTime = gatewayRoute.getCreatedTime();
|
||||
this.updatedBy = gatewayRoute.getUpdatedBy();
|
||||
this.updatedTime = gatewayRoute.getUpdatedTime();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
this.filters = objectMapper.readValue(gatewayRoute.getFilters(), new TypeReference<List<FilterDefinition>>() {
|
||||
});
|
||||
this.predicates = objectMapper.readValue(gatewayRoute.getPredicates(), new TypeReference<List<PredicateDefinition>>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
log.error("网关路由对象转换失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.param;
|
||||
|
||||
import com.springboot.cloud.common.web.entity.param.BaseParam;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GatewayRouteQueryParam extends BaseParam<GatewayRoute> {
|
||||
private String uri;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.po;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FilterDefinition {
|
||||
private String name;
|
||||
private Map<String, String> args = new LinkedHashMap<>();
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.po;
|
||||
|
||||
import com.springboot.cloud.common.web.entity.po.BasePo;
|
||||
import lombok.*;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GatewayRoute extends BasePo {
|
||||
private String uri;
|
||||
private String routeId;
|
||||
private String predicates;
|
||||
private String filters;
|
||||
private String description;
|
||||
private Integer orders = 0;
|
||||
private String status = "Y";
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.cloud.gateway.admin.entity.po;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PredicateDefinition {
|
||||
private String name;
|
||||
private Map<String, String> args = new LinkedHashMap<>();
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.springboot.cloud.gateway.admin.events;
|
||||
|
||||
import com.springboot.cloud.gateway.admin.config.BusConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class EventSender {
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Autowired
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
rabbitTemplate.setMessageConverter(messageConverter);
|
||||
}
|
||||
|
||||
public void send(String routingKey, Object object) {
|
||||
log.info("routingKey:{}=>message:{}", routingKey, object);
|
||||
rabbitTemplate.convertAndSend(BusConfig.EXCHANGE_NAME, routingKey, object);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.springboot.cloud.gateway.admin.exception;
|
||||
|
||||
import com.springboot.cloud.common.web.exception.DefaultGlobalExceptionHandlerAdvice;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandlerAdvice extends DefaultGlobalExceptionHandlerAdvice {
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package com.springboot.cloud.gateway.admin.rest;
|
||||
|
||||
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||
import com.springboot.cloud.gateway.admin.entity.form.GatewayRouteForm;
|
||||
import com.springboot.cloud.gateway.admin.entity.form.GatewayRouteQueryForm;
|
||||
import com.springboot.cloud.gateway.admin.entity.ov.GatewayRouteVo;
|
||||
import com.springboot.cloud.gateway.admin.entity.param.GatewayRouteQueryParam;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import com.springboot.cloud.gateway.admin.service.IGatewayRouteService;
|
||||
import io.swagger.annotations.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/gateway/routes")
|
||||
@Api("gateway routes")
|
||||
@Slf4j
|
||||
public class GatewayRouteController {
|
||||
|
||||
@Autowired
|
||||
private IGatewayRouteService gatewayRoutService;
|
||||
|
||||
@ApiOperation(value = "新增网关路由", notes = "新增一个网关路由")
|
||||
@ApiImplicitParam(name = "gatewayRoutForm", value = "新增网关路由form表单", required = true, dataType = "GatewayRouteForm")
|
||||
@PostMapping
|
||||
public Result add(@Valid @RequestBody GatewayRouteForm gatewayRoutForm) {
|
||||
log.info("name:", gatewayRoutForm);
|
||||
GatewayRoute gatewayRout = gatewayRoutForm.toPo(GatewayRoute.class);
|
||||
return Result.success(gatewayRoutService.add(gatewayRout));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除网关路由", notes = "根据url的id来指定删除对象")
|
||||
@ApiImplicitParam(paramType = "path", name = "id", value = "网关路由ID", required = true, dataType = "string")
|
||||
@DeleteMapping(value = "/{id}")
|
||||
public Result delete(@PathVariable String id) {
|
||||
return Result.success(gatewayRoutService.delete(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "修改网关路由", notes = "修改指定网关路由信息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "id", value = "网关路由ID", required = true, dataType = "string"),
|
||||
@ApiImplicitParam(name = "gatewayRoutForm", value = "网关路由实体", required = true, dataType = "GatewayRouteForm")
|
||||
})
|
||||
@PutMapping(value = "/{id}")
|
||||
public Result update(@PathVariable String id, @Valid @RequestBody GatewayRouteForm gatewayRoutForm) {
|
||||
GatewayRoute gatewayRout = gatewayRoutForm.toPo(GatewayRoute.class);
|
||||
gatewayRout.setId(id);
|
||||
return Result.success(gatewayRoutService.update(gatewayRout));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取网关路由", notes = "根据id获取指定网关路由信息")
|
||||
@ApiImplicitParam(paramType = "path", name = "id", value = "网关路由ID", required = true, dataType = "string")
|
||||
@GetMapping(value = "/{id}")
|
||||
public Result get(@PathVariable String id) {
|
||||
log.info("get with id:{}", id);
|
||||
return Result.success(new GatewayRouteVo(gatewayRoutService.get(id)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "根据uri获取网关路由", notes = "根据uri获取网关路由信息,简单查询")
|
||||
@ApiImplicitParam(paramType = "query", name = "name", value = "网关路由路径", required = true, dataType = "string")
|
||||
@ApiResponses(
|
||||
@ApiResponse(code = 200, message = "处理成功", response = Result.class)
|
||||
)
|
||||
@GetMapping
|
||||
public Result getByUri(@RequestParam String uri) {
|
||||
return Result.success(gatewayRoutService.query(new GatewayRouteQueryParam(uri)).stream().findFirst());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "搜索网关路由", notes = "根据条件查询网关路由信息")
|
||||
@ApiImplicitParam(name = "gatewayRoutQueryForm", value = "网关路由查询参数", required = true, dataType = "GatewayRouteQueryForm")
|
||||
@ApiResponses(
|
||||
@ApiResponse(code = 200, message = "处理成功", response = Result.class)
|
||||
)
|
||||
@PostMapping(value = "/conditions")
|
||||
public Result search(@Valid @RequestBody GatewayRouteQueryForm gatewayRouteQueryForm) {
|
||||
return Result.success(gatewayRoutService.query(gatewayRouteQueryForm.toParam(GatewayRouteQueryParam.class)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "重载网关路由", notes = "将所以网关的路由全部重载到redis中")
|
||||
@ApiResponses(
|
||||
@ApiResponse(code = 200, message = "处理成功", response = Result.class)
|
||||
)
|
||||
@PostMapping(value = "/overload")
|
||||
public Result overload() {
|
||||
return Result.success(gatewayRoutService.overload());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.springboot.cloud.gateway.admin.service;
|
||||
|
||||
import com.springboot.cloud.gateway.admin.entity.ov.GatewayRouteVo;
|
||||
import com.springboot.cloud.gateway.admin.entity.param.GatewayRouteQueryParam;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IGatewayRouteService {
|
||||
/**
|
||||
* 获取网关路由
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
GatewayRoute get(String id);
|
||||
|
||||
/**
|
||||
* 新增网关路由
|
||||
*
|
||||
* @param gatewayRoute
|
||||
* @return
|
||||
*/
|
||||
boolean add(GatewayRoute gatewayRoute);
|
||||
|
||||
/**
|
||||
* 查询网关路由
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<GatewayRouteVo> query(GatewayRouteQueryParam gatewayRouteQueryParam);
|
||||
|
||||
/**
|
||||
* 更新网关路由信息
|
||||
*
|
||||
* @param gatewayRoute
|
||||
*/
|
||||
boolean update(GatewayRoute gatewayRoute);
|
||||
|
||||
/**
|
||||
* 根据id删除网关路由
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
boolean delete(String id);
|
||||
|
||||
/**
|
||||
* 重新加载网关路由配置到redis
|
||||
*
|
||||
* @return 成功返回true
|
||||
*/
|
||||
boolean overload();
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
package com.springboot.cloud.gateway.admin.service.impl;
|
||||
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.CreateCache;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.springboot.cloud.gateway.admin.config.BusConfig;
|
||||
import com.springboot.cloud.gateway.admin.dao.GatewayRouteMapper;
|
||||
import com.springboot.cloud.gateway.admin.entity.ov.GatewayRouteVo;
|
||||
import com.springboot.cloud.gateway.admin.entity.param.GatewayRouteQueryParam;
|
||||
import com.springboot.cloud.gateway.admin.entity.po.GatewayRoute;
|
||||
import com.springboot.cloud.gateway.admin.events.EventSender;
|
||||
import com.springboot.cloud.gateway.admin.service.IGatewayRouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class GatewayRouteService extends ServiceImpl<GatewayRouteMapper, GatewayRoute> implements IGatewayRouteService {
|
||||
|
||||
private static final String GATEWAY_ROUTES = "gateway_routes::";
|
||||
|
||||
@CreateCache(name = GATEWAY_ROUTES, cacheType = CacheType.REMOTE)
|
||||
private Cache<String, RouteDefinition> gatewayRouteCache;
|
||||
|
||||
@Autowired
|
||||
private EventSender eventSender;
|
||||
|
||||
@Override
|
||||
public boolean add(GatewayRoute gatewayRoute) {
|
||||
boolean isSuccess = this.save(gatewayRoute);
|
||||
// 转化为gateway需要的类型,缓存到redis, 并事件通知各网关应用
|
||||
RouteDefinition routeDefinition = gatewayRouteToRouteDefinition(gatewayRoute);
|
||||
gatewayRouteCache.put(gatewayRoute.getRouteId(), routeDefinition);
|
||||
eventSender.send(BusConfig.ROUTING_KEY, routeDefinition);
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(String id) {
|
||||
GatewayRoute gatewayRoute = this.getById(id);
|
||||
// 删除redis缓存, 并事件通知各网关应用
|
||||
gatewayRouteCache.remove(gatewayRoute.getRouteId());
|
||||
eventSender.send(BusConfig.ROUTING_KEY, gatewayRouteToRouteDefinition(gatewayRoute));
|
||||
return this.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(GatewayRoute gatewayRoute) {
|
||||
boolean isSuccess = this.updateById(gatewayRoute);
|
||||
// 转化为gateway需要的类型,缓存到redis, 并事件通知各网关应用
|
||||
RouteDefinition routeDefinition = gatewayRouteToRouteDefinition(gatewayRoute);
|
||||
gatewayRouteCache.put(gatewayRoute.getRouteId(), routeDefinition);
|
||||
eventSender.send(BusConfig.ROUTING_KEY, routeDefinition);
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据库中json对象转换为网关需要的RouteDefinition对象
|
||||
*
|
||||
* @param gatewayRoute
|
||||
* @return RouteDefinition
|
||||
*/
|
||||
private RouteDefinition gatewayRouteToRouteDefinition(GatewayRoute gatewayRoute) {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(gatewayRoute.getRouteId());
|
||||
routeDefinition.setOrder(gatewayRoute.getOrders());
|
||||
routeDefinition.setUri(URI.create(gatewayRoute.getUri()));
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
routeDefinition.setFilters(objectMapper.readValue(gatewayRoute.getFilters(), new TypeReference<List<FilterDefinition>>() {
|
||||
}));
|
||||
routeDefinition.setPredicates(objectMapper.readValue(gatewayRoute.getPredicates(), new TypeReference<List<PredicateDefinition>>() {
|
||||
}));
|
||||
} catch (IOException e) {
|
||||
log.error("网关路由对象转换失败", e);
|
||||
}
|
||||
return routeDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayRoute get(String id) {
|
||||
return this.getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GatewayRouteVo> query(GatewayRouteQueryParam gatewayRouteQueryParam) {
|
||||
QueryWrapper<GatewayRoute> queryWrapper = gatewayRouteQueryParam.build();
|
||||
queryWrapper.eq(StringUtils.isNotBlank(gatewayRouteQueryParam.getUri()), "uri", gatewayRouteQueryParam.getUri());
|
||||
return this.list(queryWrapper).stream().map(GatewayRouteVo::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public boolean overload() {
|
||||
List<GatewayRoute> gatewayRoutes = this.list(new QueryWrapper<>());
|
||||
gatewayRoutes.forEach(gatewayRoute ->
|
||||
gatewayRouteCache.put(gatewayRoute.getRouteId(), gatewayRouteToRouteDefinition(gatewayRoute))
|
||||
);
|
||||
log.info("全局初使化网关路由成功!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.cloud.gateway.admin;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class GatewayAdminApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
25
gateway/gateway-web/.gitignore
vendored
Normal file
25
gateway/gateway-web/.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
79
gateway/gateway-web/pom.xml
Normal file
79
gateway/gateway-web/pom.xml
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>gateway-web</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>gateway-web</name>
|
||||
<description>Demo Gateway Api project for Spring Boot</description>
|
||||
|
||||
<parent>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>gateway</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<!--core基础类库-->
|
||||
<dependency>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--api网关-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
<!--redis限流-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
|
||||
</dependency>
|
||||
<!--swagger文档-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
<!--网关签权客户端-->
|
||||
<dependency>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>authentication-client</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>gateway-web</finalName>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,23 @@
|
||||
package com.springboot.cloud.gateway;
|
||||
|
||||
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
|
||||
import com.alicp.jetcache.anno.config.EnableMethodCache;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients(basePackages = "com.springboot.cloud.auth.client")
|
||||
@EnableCircuitBreaker
|
||||
@EnableMethodCache(basePackages = "com.springboot.cloud")
|
||||
@EnableCreateCacheAnnotation
|
||||
public class GatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.springboot.cloud.gateway.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.springboot.cloud.gateway.events.BusReceiver;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
|
||||
import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class BusConfig {
|
||||
|
||||
private static final String EXCHANGE_NAME = "spring-boot-exchange";
|
||||
private static final String ROUTING_KEY = "gateway-route";
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String appName;
|
||||
|
||||
@Bean
|
||||
Queue queue() {
|
||||
String queueName = new Base64UrlNamingStrategy(appName + ".").generateName();
|
||||
log.info("queue name:{}", queueName);
|
||||
return new Queue(queueName, false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
TopicExchange exchange() {
|
||||
log.info("exchange:{}", EXCHANGE_NAME);
|
||||
return new TopicExchange(EXCHANGE_NAME);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Binding binding(Queue queue, TopicExchange exchange) {
|
||||
log.info("binding {} to {} with {}", queue, exchange, ROUTING_KEY);
|
||||
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter, Queue queue) {
|
||||
log.info("init simpleMessageListenerContainer: {}", queue.getName());
|
||||
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
|
||||
container.setQueueNames(queue.getName());
|
||||
container.setMessageListener(messageListenerAdapter);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MessageListenerAdapter messageListenerAdapter(BusReceiver busReceiver, MessageConverter messageConverter) {
|
||||
log.info("new listener");
|
||||
return new MessageListenerAdapter(busReceiver, messageConverter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageConverter messageConverter() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.springboot.cloud.gateway.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.validation.Validator;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class DefaultRedisRateLimiter extends RedisRateLimiter {
|
||||
|
||||
Config getDefaultConfig() {
|
||||
return super.getConfig().get("defaultFilters");
|
||||
}
|
||||
|
||||
public DefaultRedisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate,
|
||||
RedisScript<List<Long>> script,
|
||||
@Qualifier("defaultValidator") Validator validator) {
|
||||
super(redisTemplate, script, validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Response> isAllowed(String routeId, String id) {
|
||||
if (null == super.getConfig().get(routeId))
|
||||
getConfig().put(routeId, getDefaultConfig());
|
||||
return super.isAllowed(routeId, id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.springboot.cloud.gateway.config;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 自定义限流标志的key,多个维度可以从这里入手
|
||||
* exchange对象中获取服务ID、请求信息,用户信息等
|
||||
*/
|
||||
@Component
|
||||
public class RequestRateLimiterConfig {
|
||||
|
||||
/**
|
||||
* ip地址限流
|
||||
*
|
||||
* @return 限流key
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public KeyResolver remoteAddressKeyResolver() {
|
||||
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求路径限流
|
||||
*
|
||||
* @return 限流key
|
||||
*/
|
||||
@Bean
|
||||
public KeyResolver apiKeyResolver() {
|
||||
return exchange -> Mono.just(exchange.getRequest().getPath().value());
|
||||
}
|
||||
|
||||
/**
|
||||
* username限流
|
||||
*
|
||||
* @return 限流key
|
||||
*/
|
||||
@Bean
|
||||
public KeyResolver userKeyResolver() {
|
||||
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.springboot.cloud.gateway.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/swagger-resources")
|
||||
public class SwaggerHandler {
|
||||
|
||||
@Autowired(required = false)
|
||||
private SecurityConfiguration securityConfiguration;
|
||||
|
||||
@Autowired(required = false)
|
||||
private UiConfiguration uiConfiguration;
|
||||
|
||||
private final SwaggerResourcesProvider swaggerResources;
|
||||
|
||||
@Autowired
|
||||
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
|
||||
this.swaggerResources = swaggerResources;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/configuration/security")
|
||||
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
|
||||
}
|
||||
|
||||
@GetMapping("/configuration/ui")
|
||||
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
|
||||
}
|
||||
|
||||
@GetMapping("")
|
||||
public Mono<ResponseEntity> swaggerResources() {
|
||||
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.springboot.cloud.gateway.config;
|
||||
|
||||
import com.springboot.cloud.gateway.service.RouteService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Primary
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class SwaggerProvider implements SwaggerResourcesProvider {
|
||||
public static final String API_URI = "/v2/api-docs";
|
||||
|
||||
@Autowired
|
||||
private final RouteService routeService;
|
||||
|
||||
@Override
|
||||
public List<SwaggerResource> get() {
|
||||
List<SwaggerResource> resources = new ArrayList<>();
|
||||
routeService.getRouteDefinitions().stream()
|
||||
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
|
||||
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
|
||||
.peek(predicateDefinition -> log.debug("路由配置参数:{}", predicateDefinition.getArgs()))
|
||||
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
|
||||
predicateDefinition.getArgs().get("pattern").replace("/**", API_URI)))));
|
||||
log.debug("resources:{}", resources);
|
||||
return resources;
|
||||
}
|
||||
|
||||
private SwaggerResource swaggerResource(String name, String location) {
|
||||
SwaggerResource swaggerResource = new SwaggerResource();
|
||||
swaggerResource.setName(name);
|
||||
swaggerResource.setSwaggerVersion("2.0");
|
||||
swaggerResource.setUrl(location);
|
||||
return swaggerResource;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.springboot.cloud.gateway.events;
|
||||
|
||||
import com.springboot.cloud.gateway.service.RouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
//TODO 待实现删除,更新操作
|
||||
@Component
|
||||
@Slf4j
|
||||
public class BusReceiver {
|
||||
|
||||
@Autowired
|
||||
private RouteService routeService;
|
||||
|
||||
public void handleMessage(RouteDefinition routeDefinition) {
|
||||
log.info("Received Message:<{}>", routeDefinition);
|
||||
routeService.save(routeDefinition);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.springboot.cloud.gateway.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
|
||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.*;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
|
||||
|
||||
@Autowired
|
||||
private GateWayExceptionHandlerAdvice gateWayExceptionHandlerAdvice;
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultErrorWebExceptionHandler} instance.
|
||||
*
|
||||
* @param errorAttributes the error attributes
|
||||
* @param resourceProperties the resources configuration properties
|
||||
* @param errorProperties the error configuration properties
|
||||
* @param applicationContext the current application context
|
||||
*/
|
||||
public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
|
||||
ErrorProperties errorProperties, ApplicationContext applicationContext) {
|
||||
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
|
||||
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
||||
Map<String, Object> error = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
|
||||
HttpStatus errorStatus = getHttpStatus(error);
|
||||
Throwable throwable = getError(request);
|
||||
return ServerResponse.status(errorStatus)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(gateWayExceptionHandlerAdvice.handle(throwable)));
|
||||
//.doOnNext((resp) -> logError(request, errorStatus));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.springboot.cloud.gateway.exception;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass(WebFluxConfigurer.class)
|
||||
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
|
||||
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
|
||||
public class ExceptionAutoConfiguration {
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ResourceProperties resourceProperties;
|
||||
|
||||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
private ServerCodecConfigurer serverCodecConfigurer;
|
||||
|
||||
public ExceptionAutoConfiguration(ServerProperties serverProperties,
|
||||
ResourceProperties resourceProperties,
|
||||
ObjectProvider<List<ViewResolver>> viewResolversProvider,
|
||||
ServerCodecConfigurer serverCodecConfigurer,
|
||||
ApplicationContext applicationContext) {
|
||||
this.serverProperties = serverProperties;
|
||||
this.applicationContext = applicationContext;
|
||||
this.resourceProperties = resourceProperties;
|
||||
this.viewResolvers = viewResolversProvider
|
||||
.getIfAvailable(() -> Collections.emptyList());
|
||||
this.serverCodecConfigurer = serverCodecConfigurer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
|
||||
DefaultErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
|
||||
errorAttributes, this.resourceProperties,
|
||||
this.serverProperties.getError(), this.applicationContext);
|
||||
exceptionHandler.setViewResolvers(this.viewResolvers);
|
||||
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
|
||||
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
|
||||
return exceptionHandler;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package com.springboot.cloud.gateway.exception;
|
||||
|
||||
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||
import com.springboot.cloud.common.core.exception.SystemErrorType;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.netty.channel.ConnectTimeoutException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GateWayExceptionHandlerAdvice {
|
||||
|
||||
@ExceptionHandler(value = {ResponseStatusException.class})
|
||||
public Result handle(ResponseStatusException ex) {
|
||||
log.error("response status exception:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.GATEWAY_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {ConnectTimeoutException.class})
|
||||
public Result handle(ConnectTimeoutException ex) {
|
||||
log.error("connect timeout exception:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.GATEWAY_CONNECT_TIME_OUT);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {NotFoundException.class})
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public Result handle(NotFoundException ex) {
|
||||
log.error("not found exception:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.GATEWAY_NOT_FOUND_SERVICE);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {ExpiredJwtException.class})
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public Result handle(ExpiredJwtException ex) {
|
||||
log.error("ExpiredJwtException:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {SignatureException.class})
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public Result handle(SignatureException ex) {
|
||||
log.error("SignatureException:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {MalformedJwtException.class})
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public Result handle(MalformedJwtException ex) {
|
||||
log.error("MalformedJwtException:{}", ex.getMessage());
|
||||
return Result.fail(SystemErrorType.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {RuntimeException.class})
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handle(RuntimeException ex) {
|
||||
log.error("runtime exception:{}", ex.getMessage());
|
||||
return Result.fail();
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {Exception.class})
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handle(Exception ex) {
|
||||
log.error("exception:{}", ex.getMessage());
|
||||
return Result.fail();
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {Throwable.class})
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handle(Throwable throwable) {
|
||||
Result result = Result.fail();
|
||||
if (throwable instanceof ResponseStatusException) {
|
||||
result = handle((ResponseStatusException) throwable);
|
||||
} else if (throwable instanceof ConnectTimeoutException) {
|
||||
result = handle((ConnectTimeoutException) throwable);
|
||||
} else if (throwable instanceof NotFoundException) {
|
||||
result = handle((NotFoundException) throwable);
|
||||
} else if (throwable instanceof RuntimeException) {
|
||||
result = handle((RuntimeException) throwable);
|
||||
} else if (throwable instanceof Exception) {
|
||||
result = handle((Exception) throwable);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.springboot.cloud.gateway.filter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.springboot.cloud.auth.client.service.IAuthService;
|
||||
import com.springboot.cloud.gateway.service.PermissionService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 请求url权限校验
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "com.springboot.cloud.auth.client")
|
||||
@Slf4j
|
||||
public class AccessGatewayFilter implements GlobalFilter {
|
||||
|
||||
private static final String X_CLIENT_TOKEN_USER = "x-client-token-user";
|
||||
private static final String X_CLIENT_TOKEN = "x-client-token";
|
||||
private static final String X_CLIENT_SECRET = "gateway-web";
|
||||
private static final String USER_CURRENT_COMPANY="company";
|
||||
|
||||
/**
|
||||
* 由authentication-client模块提供签权的feign客户端
|
||||
*/
|
||||
@Autowired
|
||||
private IAuthService authService;
|
||||
|
||||
@Autowired
|
||||
private PermissionService permissionService;
|
||||
|
||||
/**
|
||||
* 1.首先网关检查token是否有效,无效直接返回401,不调用签权服务
|
||||
* 2.调用签权服务器看是否对该请求有权限,有权限进入下一个filter,没有权限返回401
|
||||
*
|
||||
* @param exchange
|
||||
* @param chain
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String authentication = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
String url = request.getPath().value();
|
||||
String method = request.getMethod().name();
|
||||
//后端识别companyId
|
||||
String companyId = Optional.ofNullable(request.getQueryParams().toSingleValueMap().get("companyId")).orElseGet(String::new);
|
||||
log.debug("url:{},headers:{}", url, request.getHeaders());
|
||||
//不需要网关签权的url
|
||||
if (authService.ignoreAuthentication(url)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
//调用签权服务看用户是否有权限,若有权限进入下一个filter
|
||||
if (permissionService.permission(companyId, authentication, url)) {
|
||||
//设置服务间调用请求头
|
||||
ServerHttpRequest.Builder builder = request.mutate();
|
||||
String originStr = new StringBuilder().append(url).append(method).append(X_CLIENT_TOKEN).append(X_CLIENT_SECRET).toString();
|
||||
String md5Secret = DigestUtils.md5DigestAsHex(originStr.getBytes());
|
||||
builder.header(X_CLIENT_TOKEN, md5Secret);
|
||||
builder.header(USER_CURRENT_COMPANY,companyId);
|
||||
//将jwt token中的用户信息传给服务
|
||||
builder.header(X_CLIENT_TOKEN_USER, getUserToken(authentication));
|
||||
return chain.filter(exchange.mutate().request(builder.build()).build());
|
||||
}
|
||||
return unauthorized(exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取jwt token中的数据,转为json
|
||||
*
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
private String getUserToken(String authentication) {
|
||||
String token = "{}";
|
||||
try {
|
||||
token = new ObjectMapper().writeValueAsString(authService.getJwt(authentication).getBody());
|
||||
return token;
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("token json error:{}", e.getMessage());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网关拒绝,返回401
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
private Mono<Void> unauthorized(ServerWebExchange serverWebExchange) {
|
||||
serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
DataBuffer buffer = serverWebExchange.getResponse()
|
||||
.bufferFactory().wrap(HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes());
|
||||
return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.springboot.cloud.gateway.filter;
|
||||
|
||||
import com.springboot.cloud.gateway.config.SwaggerProvider;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
//@Component
|
||||
@Slf4j
|
||||
public class SwaggerHeaderFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final String HEADER_NAME = "X-Forwarded-Prefix";
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String path = request.getURI().getPath();
|
||||
if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
|
||||
log.info("basePath: {}", basePath);
|
||||
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
|
||||
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
|
||||
return chain.filter(newExchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -200;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.springboot.cloud.gateway.routes;
|
||||
|
||||
import com.springboot.cloud.gateway.service.RouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
|
||||
|
||||
@Autowired
|
||||
private RouteService routeService;
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(routeService.getRouteDefinitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(Mono<RouteDefinition> route) {
|
||||
return route.flatMap(routeDefinition -> {
|
||||
routeService.save(routeDefinition);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> delete(Mono<String> routeId) {
|
||||
return routeId.flatMap(id -> {
|
||||
routeService.delete(id);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.springboot.cloud.gateway.service;
|
||||
|
||||
public interface PermissionService {
|
||||
/**
|
||||
*
|
||||
* @param companyId
|
||||
* @param authentication
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
boolean permission(String companyId,String authentication, String url);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.springboot.cloud.gateway.service;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface RouteService {
|
||||
Collection<RouteDefinition> getRouteDefinitions();
|
||||
|
||||
boolean save(RouteDefinition routeDefinition);
|
||||
|
||||
boolean delete(String routeId);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.springboot.cloud.gateway.service.impl;
|
||||
|
||||
import com.springboot.cloud.auth.client.service.IAuthService;
|
||||
import com.springboot.cloud.gateway.service.PermissionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
/**
|
||||
* 由authentication-client模块提供签权的feign客户端
|
||||
*/
|
||||
@Autowired
|
||||
private IAuthService authService;
|
||||
|
||||
@Override
|
||||
public boolean permission(String companyId, String authentication, String url) {
|
||||
return authService.hasPermission(companyId,authentication, url);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.springboot.cloud.gateway.service.impl;
|
||||
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.CreateCache;
|
||||
import com.springboot.cloud.gateway.service.RouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class RouteServiceImpl implements RouteService {
|
||||
|
||||
private static final String GATEWAY_ROUTES = "gateway_routes::";
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@CreateCache(name = GATEWAY_ROUTES, cacheType = CacheType.REMOTE)
|
||||
private Cache<String, RouteDefinition> gatewayRouteCache;
|
||||
|
||||
private Map<String, RouteDefinition> routeDefinitionMaps = new HashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
private void loadRouteDefinition() {
|
||||
log.info("loadRouteDefinition, 开始初使化路由");
|
||||
Set<String> gatewayKeys = stringRedisTemplate.keys(GATEWAY_ROUTES + "*");
|
||||
if (CollectionUtils.isEmpty(gatewayKeys)) {
|
||||
return;
|
||||
}
|
||||
log.info("预计初使化路由, gatewayKeys:{}", gatewayKeys);
|
||||
// 去掉key的前缀
|
||||
Set<String> gatewayKeyIds = gatewayKeys.stream().map(key -> {
|
||||
return key.replace(GATEWAY_ROUTES, StringUtils.EMPTY);
|
||||
}).collect(Collectors.toSet());
|
||||
Map<String, RouteDefinition> allRoutes = gatewayRouteCache.getAll(gatewayKeyIds);
|
||||
log.info("gatewayKeys:{}", allRoutes);
|
||||
// 以下代码原因是,jetcache将RouteDefinition返序列化后,uri发生变化,未初使化,导致路由异常,以下代码是重新初使化uri
|
||||
allRoutes.values().forEach(routeDefinition -> {
|
||||
try {
|
||||
routeDefinition.setUri(new URI(routeDefinition.getUri().toASCIIString()));
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("网关加载RouteDefinition异常:", e);
|
||||
}
|
||||
});
|
||||
routeDefinitionMaps.putAll(allRoutes);
|
||||
log.info("共初使化路由信息:{}", routeDefinitionMaps.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RouteDefinition> getRouteDefinitions() {
|
||||
return routeDefinitionMaps.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(RouteDefinition routeDefinition) {
|
||||
routeDefinitionMaps.put(routeDefinition.getId(), routeDefinition);
|
||||
log.info("新增路由1条:{},目前路由共{}条", routeDefinition, routeDefinitionMaps.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(String routeId) {
|
||||
routeDefinitionMaps.remove(routeId);
|
||||
log.info("删除路由1条:{},目前路由共{}条", routeId, routeDefinitionMaps.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.springboot.cloud.gateway;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
public class GatewayApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
|
||||
}
|
||||
}
|
||||
41
gateway/pom.xml
Normal file
41
gateway/pom.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>xin-cloud</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>gateway</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>gateway</name>
|
||||
<description>Demo Gateway project for Spring Cloud</description>
|
||||
|
||||
<modules>
|
||||
<module>gateway-web</module>
|
||||
<module>gateway-admin</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alicp.jetcache</groupId>
|
||||
<artifactId>jetcache-starter-redis</artifactId>
|
||||
<version>2.5.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
Reference in New Issue
Block a user