为什么使用网关?
因为多个微服务的端口不同,前端调用不方便,使用网关可以统一接收处理前端的请求,同时方便接口的集中处理,比如鉴权、聚合接口文档、限流等等..
这里使用Knife4j文档工具来实现接口文档:Knife4j框架相关的blog | Knife4j
使用
- pom.xml引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 配置application.yml配置文件:
spring:
cloud:
gateway:
routes:
- id: backend-user-service
uri: lb://backend-user-service
predicates:
- Path=/api/user/**
- id: stuoj-backend-question-service
uri: lb://backend-question-service
predicates:
- Path=/api/question/**
- id: backend-comment-service
uri: lb://backend-comment-service
predicates:
- Path=/api/comment/**
- id: backend-xxx-service
uri: lb://backend-xxx-service
predicates:
- Path=/api/xxx/**
nacos:
discovery:
server-addr: localhost:8848
application:
name: backend-gateway
main:
web-application-type: reactive
server:
port: 9090
聚合接口文档
目的:以一个全局的视角去集中管理里接口文档(Knife4j)
- 先给所有的服务引入依赖。同时开启接口文档配置
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
knife4j:
enable: true
- 给网关服务配置集中管理文档
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
knife4j:
gateway:
# ① 第一个配置,开启gateway聚合组件
enabled: true
# ② 第二行配置,设置聚合模式采用discover服务发现的模式
strategy: discover
discover:
# ③ 第三行配置,开启discover模式
enabled: true
# ④ 第四行配置,聚合子服务全部为Swagger2规范的文档
version: swagger2
之后就可以获得所有服务的文档了:
网关集中解决跨域
在gateway的模块下创建一个confg配置代码:
package com.stukk.stuojbackendgateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
import java.util.Arrays;
/**
* @Author: stukk
* @Description: 跨域处理
* @DateTime: 2023-12-07 19:04
**/
@Configuration
public class CrosConfig {
@Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowedOriginPatterns(Arrays.asList("*"));
corsConfiguration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
网关实现权限校验
使用拦截器Filter,拦截路径并判断权限
package com.stukk.stuojbackendgateway.filter;
import cn.hutool.core.text.AntPathMatcher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @Author: stukk
* @Description: 网关拦截器
* @DateTime: 2023-12-07 19:16
**/
@Component
public class GlobalAuthFilter implements GlobalFilter {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 判断是不是内部调用,拦截内部调用
if(antPathMatcher.match("**/inner/**",path)){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
DataBufferFactory dataBufferFactory = response.bufferFactory();
DataBuffer dataBuffer = dataBufferFactory.wrap("无权限".getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
return chain.filter(exchange);
}
}