一、功能
-
路由转发
-
依赖
<dependencies> <!-- SpringBoot Actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- SpringCloud Gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies>
-
配置
spring: cloud: gateway: routes: # 路由的唯一标识 - id: stackdin-test # 路由转发的地址 uri: http://127.0.0.1:9002 # 断言工厂 predicates: - Path=/stackdin-test/** # 过滤器 filters: - StripPrefix=1
-
-
网关鉴权
-
实现org.springframework.cloud.gateway.filter.GlobalFilter接口,重写filter方法
import com.alibaba.fastjson2.JSON; import com.auth0.jwt.interfaces.Claim; import com.diancan.common.core.constant.CacheConstants; import com.diancan.common.core.constant.SecurityConstants; import com.diancan.common.core.domain.LoginUser; import com.diancan.common.core.utils.JwtUtils; import com.diancan.common.core.utils.ServletUtils; import com.diancan.common.core.utils.StringUtils; import com.diancan.common.redis.service.RedisService; import com.diancan.gateway.config.properties.IgnoreWhiteProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Map; /** * 网关鉴权 */ @Component public class AuthFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); // 排除过滤的 uri 地址 @Autowired private IgnoreWhiteProperties ignoreWhite; @Autowired private RedisService redisService; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest.Builder mutate = request.mutate(); String url = request.getURI().getPath(); // 跳过不需要验证的路径 if (StringUtils.matches(url, ignoreWhite.getWhites())) { return chain.filter(exchange); } //获取token String token = ServletUtils.getToken(request); //token校验 if (StringUtils.isEmpty(token)) { return unauthorizedResponse(exchange, "令牌不能为空"); } //JWT校验 Map<String, Claim> claims; try { claims = JwtUtils.parseToken(token); } catch (Exception e) { return unauthorizedResponse(exchange, "令牌已过期或验证不正确"); } if (null == claims) { return unauthorizedResponse(exchange,"令牌已过期或验证不正确"); } //获取用户信息 LoginUser loginUser = JSON.parseObject(claims.get(SecurityConstants.USER).asString(),LoginUser.class); //redis检验 boolean islogin = redisService.hasKey(getRedisTokenKey(loginUser.getUserKey())); if (!islogin) { return unauthorizedResponse(exchange, "登录状态已过期"); } // 设置用户信息到请求 ServletUtils.addHeader(mutate, SecurityConstants.USER_KEY, loginUser.getUserKey()); ServletUtils.addHeader(mutate, SecurityConstants.USER_ID, loginUser.getUserId()); ServletUtils.addHeader(mutate, SecurityConstants.USER_NAME, loginUser.getUserName()); // 内部请求来源参数清除 ServletUtils.removeHeader(mutate, SecurityConstants.FROM_SOURCE); return chain.filter(exchange.mutate().request(mutate.build()).build()); } private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) { return ServletUtils.unauthorizedResponse(exchange, msg); } /** * 获取缓存key */ private String getRedisTokenKey(String token) { return CacheConstants.LOGIN_TOKEN_KEY + token; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
-
-
统一异常
-
实现org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler,重写handle方法
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; import org.springframework.cloud.gateway.support.NotFoundException; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import com.diancan.common.core.utils.ServletUtils; import reactor.core.publisher.Mono; /** * 网关统一异常处理 */ @Order(-1) @Configuration public class GatewayExceptionHandler implements ErrorWebExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); @Override public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { ServerHttpResponse response = exchange.getResponse(); if (exchange.getResponse().isCommitted()) { return Mono.error(ex); } String msg; if (ex instanceof NotFoundException) { msg = "服务未找到"; } else if (ex instanceof ResponseStatusException responseStatusException) { msg = responseStatusException.getMessage(); } else { msg = "内部服务器错误"; } log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); return ServletUtils.webFluxResponseWriter(response, msg); } }
-
-
熔断限流
-
依赖
<!-- SpringBoot Actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- SpringCloud Gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- SpringCloud Alibaba Sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- SpringCloud Alibaba Sentinel Gateway --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency>
-
配置
# Spring spring: application: # 应用名称 name: stackdin-gateway profiles: # 环境配置 active: dev cloud: gateway: discovery: locator: # 配置服务ID转换为小写 lower-case-service-id: true # 启用云网关的发现功能 enabled: true routes: # 路由的唯一标识 - id: stackdin-test # 路由转发的地址 uri: http://127.0.0.1:9002 # 断言工厂 predicates: - Path=/stackdin-test/** # 过滤器 filters: - StripPrefix=1 sentinel: # 取消控制台懒加载 eager: true transport: # 控制台地址 dashboard: localhost:8080
-
自定义限流异常
/** * 自定义限流异常处理 */ public class SentinelFallbackHandler implements WebExceptionHandler { @Override public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { //检查响应是否已经提交,如果已经提交,则无法处理,直接返回异常 if (exchange.getResponse().isCommitted()) { return Mono.error(ex); } //检查异常是否是 BlockException,如果不是,则无法处理,直接返回异常 if (!BlockException.isBlockException(ex)) { return Mono.error(ex); } //处理被阻塞的请求 return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); } private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) { return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); } private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) { //向客户端写入响应信息。当请求超过最大数时,返回给客户端信息。 return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); } } /** * 网关限流配置 */ @Configuration public class GatewayConfig { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelFallbackHandler sentinelGatewayExceptionHandler() { return new SentinelFallbackHandler(); } }
-
-
knife4j接口文档统一整合
-
依赖
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency>
-
实现springfox.documentation.swagger.web.SwaggerResourcesProvider,重写get方法聚合其他服务接口
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; import org.springframework.cloud.gateway.support.NotFoundException; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import com.diancan.common.core.utils.ServletUtils; import reactor.core.publisher.Mono; /** * 网关统一异常处理 */ @Order(-1) @Configuration public class GatewayExceptionHandler implements ErrorWebExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); @Override public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) { ServerHttpResponse response = exchange.getResponse(); if (exchange.getResponse().isCommitted()) { return Mono.error(ex); } String msg; if (ex instanceof NotFoundException) { msg = "服务未找到"; } else if (ex instanceof ResponseStatusException responseStatusException) { msg = responseStatusException.getMessage(); } else { msg = "内部服务器错误"; } log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); return ServletUtils.webFluxResponseWriter(response, msg); } }
-
重写并覆盖/swagger-resources接口,由网关的注册中心动态发现所有的微服务文档
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)); } @SuppressWarnings("rawtypes") @GetMapping("") public Mono<ResponseEntity> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
-