feat:first commit gateway

This commit is contained in:
xiang
2025-09-05 23:21:19 +08:00
parent ed7c233625
commit 014b3edf88
8 changed files with 266 additions and 5 deletions

84
pom.xml
View File

@@ -21,9 +21,87 @@
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<spring.boot.version>2.7.18</spring.boot.version>
<spring.cloud.version>2021.0.8</spring.cloud.version>
<spring.cloud.alibaba.version>2021.1</spring.cloud.alibaba.version>
</properties>
<dependencies>
<!-- Spring Cloud Gateway核心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.8</version>
</dependency>
<!-- Spring Boot WebFlux必须Netty 容器) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
<!-- Actuator可选健康检查 & 调试路由) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!-- Lombok可选简化开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 编译器插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -17,4 +17,12 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.xiang</groupId>
<artifactId>xs-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -2,12 +2,14 @@ package com.xiang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
/**
* @Author: xiang
* @Date: 2025-08-12 17:26
*/
@SpringBootApplication
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class XsGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(XsGatewayApplication.class, args);

View File

@@ -0,0 +1,30 @@
package com.xiang.xservice.gateway.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ServiceDiscoveryController {
@Autowired
private DiscoveryClient discoveryClient;
/**
* 查看所有注册到 Nacos 的服务
*/
@GetMapping("/services")
public List<String> getServices() {
return discoveryClient.getServices();
}
/**
* 查看某个服务的实例信息
*/
@GetMapping("/services/instances")
public Object getServiceInstances(String serviceName) {
return discoveryClient.getInstances(serviceName);
}
}

View File

@@ -0,0 +1,40 @@
server:
port: 38010
spring:
application:
name: xservice-gateway
main:
web-application-type: reactive # 强制使用 WebFlux
cloud:
nacos:
discovery:
server-addr: general.xiangtech.xyz:8848
namespace: 00131110-3ecb-4a35-8bbb-624edde1d937
group: DEFAULT_GROUP
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: true # 开启 nacos 自动路由
routes:
- id: xservice-auth-center
uri: lb://xservice-auth-center
predicates:
- Path=/api-auth/**
filters:
- StripPrefix=1
redis:
host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com
port: 6379
password: Xiang0000 # 如果无密码可以省略
database: 0
timeout: 5000
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000

View File

@@ -4,3 +4,5 @@ server:
spring:
profiles:
active: local
main:
web-application-type: reactive

View File

@@ -0,0 +1,59 @@
package com.xiang.xservice.gateway.service.core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 放行 /public/** 和 /login
if (path.contains("/public/") || path.contains("/login")) {
log.debug("跳过 Token 校验: {}", path);
return chain.filter(exchange);
}
// 获取 Header 中的 Authorization
String token = request.getHeaders().getFirst("Authorization");
if (!StringUtils.hasText(token)) {
log.warn("❌ 缺少 Token: {}", path);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
try {
// TODO: 这里换成你自己的 JwtUtil.verify(token)
// boolean valid = JwtUtil.verify(token);
if (false) {
log.warn("❌ Token 校验失败: {}", token);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
} catch (Exception e) {
log.error("❌ Token 校验异常", e);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
log.info("✅ Token 校验通过: {}", token);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}

View File

@@ -0,0 +1,42 @@
package com.xiang.xservice.gateway.service.core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
@Slf4j
@Component
public class LoggerGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("🌐 Gateway 请求进入, 原始请求路径:{}", exchange.getRequest().getURI().getPath());
ServerHttpRequest request = exchange.getRequest();
InetSocketAddress remoteAddress = request.getRemoteAddress();
log.info("请求路径: {} | 方法: {} | 来源: {} | 匹配到路由:{}, 转发目标地址:{}",
request.getURI(),
request.getMethod(),
remoteAddress != null ? remoteAddress.getHostString() : "unknown",
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR),
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR));
// 继续执行过滤链
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
log.info("响应状态: {}", exchange.getResponse().getStatusCode());
}));
}
@Override
public int getOrder() {
return 0;
}
}