04-服务网关

一、功能

  1. 路由转发

    1. 依赖

      <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>
    2. 配置

      spring: 
        cloud:
          gateway:
            routes:
              # 路由的唯一标识
              - id: stackdin-test
                # 路由转发的地址
                uri: http://127.0.0.1:9002
                # 断言工厂
                predicates:
                  - Path=/stackdin-test/**
                # 过滤器
                filters:
                  - StripPrefix=1
  2. 网关鉴权

    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;
          }
      }
  3. 统一异常

    1. 实现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);
          }
      }
  4. 熔断限流

    1. 依赖

       <!-- 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>
    2. 配置

      # 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
    3. 自定义限流异常

      /**
       * 自定义限流异常处理
       */
      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();
          }
      }
  5. knife4j接口文档统一整合

    1. 依赖

      <dependency>
                  <groupId>com.github.xiaoymin</groupId>
                  <artifactId>knife4j-spring-boot-starter</artifactId>
              </dependency>
    2. 实现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);
          }
      }
    3. 重写并覆盖/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)));
          }
      }
      

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值