小白操作:zuul网关升级为getway网关,以及jwt的使用

首先,了解一下两个网关的一些区别。
zool是同步getway是异步进行
两者均是web网关,处理的是http请求
gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件,而zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等
gateway很好的支持异步,而zuul仅支持同步,那么理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定
从框架设计的角度看,gateway具有更好的扩展性,并且其已经发布了2.0.0的RELESE版本,稳定性也是非常好的
编码上看,zuul更加简洁易懂,注释规范清晰,而gateway作为Spring家族的一份子,竟然几乎不注释…
总的来说,在微服务架构,如果使用了Spring Cloud生态的基础组件,则Spring Cloud Gateway相比而言更加具备优势,单从流式编程+支持异步上就足以让开发者选择它了。
大佬总结链接:https://blog.csdn.net/u010681191/article/details/99656413

现在可以开始正式项目配置getway
添加相应的依赖

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

这里一起引用了相应的ribbon与hystrix的依赖,在引用依赖的时候要注意:
如果启动项目的时候出错:Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type ‘org.springframework.http.codec.ServerCodecConfigurer’ that could not be found.

原因是getway网关的依赖与web依赖是有冲突的,应该将项目中web依赖去除~

 			<exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
                </exclusions>

配置文件

server:
  port: 8399
spring:
  application:
    name: gblx-gateway
  cloud:
    config:
      discovery:
        enabled: true
        service-id: gblx-config
      #uri: http://localhost:8334
      profile: test
      name: redis

    gateway:
      #允许跨域访问
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
      #表明Gateway开启服务注册和发现的功能,并且Spring Cloud Gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
      #spring.cloud.gateway.discovery.locator.lowerCaseServiceId是将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了)。
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      #定义路由规则
      routes:
        - id: gblx-user
          uri: lb://gblx-user
          predicates:
            - Path=/user/s**
          filters:
            - StripPrefix=1
            #定义熔断的方法和路径
            - name: Hystrix
              args:
                name: fallbackCommand
                fallbackUri: forward:/web/fallback
            #定义redis限流
            - name: RequestRateLimiter
              args:
                #令牌通每秒填充平均速率
                redis-rate-limiter.replenishRate: 5
                #令牌桶的总容量。
                redis-rate-limiter.burstCapacity: 50
                rate-limiter: "#{@defaultRedisRateLimiter}"
                #用于限流的解析器的Bean对象的名字。它使用SpEL表达式#{@beanName}从Spring容器中获取bean对象。
                key-resolver: "#{@apiKeyResolver}"
        - id: gblx-order
          uri: lb://gblx-order
          predicates:
            - Path=/clearing/**
          filters:
            - StripPrefix=1

配置相应的过滤器,需要继承两个类GlobalFilter, Ordered


```java
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    private static final String TOKEN = "token";

    /**
     * 过滤的请求路径
     */
    @Value("${auth.skip.urls}")
    private String[] skipAuthUrls;

    @Override
    public int getOrder() {
        return 1;
    }

    @Autowired
    private JedisCluster jedisCluster;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put("startTime", System.currentTimeMillis());
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取请求方式post、get
        String method = request.getMethodValue();
        //接口请求路径
        String url = request.getURI().getPath();
        //拿到token
        String token = request.getHeaders().getFirst(TOKEN);
        //跳过不需要验证的路径
        if (Arrays.asList(skipAuthUrls).contains(url)) {
            exchange.getResponse().setStatusCode(HttpStatus.OK);
            //有些是可带可不带token的接口,判断是否带token转化成功用户id
            if (StringUtils.isNotBlank(token)) {
                Auth auth = TokenUtils.decodeToken(token);
                if (auth != null) {
                    return returnMono(chain, exchange, auth, url, method);
                }
            }
            return chain.filter(exchange);
        }
        //token为空
        if (StringUtils.isBlank(token)) {
            return unauthorized(exchange);
        }

        //根据传入的APPID获取USER信息用户对象转换为字符串.
        Auth auth = TokenUtils.decodeToken(token);
        if (null == auth) {
            return unauthorized(exchange);
        }
        //判断是否是最新的token   传入的token  --> redis的token
        String redisToken = jedisCluster.hget(CacheKeyForRedis.LOGIN, auth.getUserId() + "");
        if (StringUtils.isBlank(redisToken)) {
            return unauthorized(exchange);
        }
        if (!StringUtils.equals(redisToken, token)) {
            return unauthorized(exchange);
        }
        return returnMono(chain, exchange, auth, url, method);
    }

    /**
     * 从Flux<DataBuffer>中获取字符串的方法
     *
     * @return 请求体
     */
    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
        //获取请求体
        Flux<DataBuffer> body = serverHttpRequest.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        //获取request body
        return bodyRef.get();
    }

    /**
     * 获取请求数据,转换为String
     *
     * @param value
     * @return
     */
    private DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    /**
     * 网关拒绝,返回401
     *
     * @param
     */
    private Mono<Void> unauthorized(ServerWebExchange serverWebExchange) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        JSONObject errorJson = new JSONObject();
        errorJson.put("status", GatewayErrorType.PARAM_NOT_KEY.getValue());
        errorJson.put("msg", GatewayErrorType.PARAM_NOT_KEY.getReasonPhrase());
        DataBuffer buffer = serverWebExchange.getResponse()
                .bufferFactory().wrap(errorJson.toJSONString().getBytes(StandardCharsets.UTF_8));
        ServerHttpResponse response = serverWebExchange.getResponse();
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Flux.just(buffer));
    }


    private Mono<Void> returnMono(GatewayFilterChain chain, ServerWebExchange exchange, Auth auth, String url, String method) {
        //所有校验通过的请求,在请求头存放对象信息
        Consumer<HttpHeaders> httpHeaders = httpHeader -> {
            httpHeader.set("userId", auth.getUserId().toString());
            httpHeader.set("account", auth.getAccount());
        };
        ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
        ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
        return chain.filter(build).then(Mono.fromRunnable(() -> {
            Long startTime = exchange.getAttribute("startTime");
            if (startTime != null) {
                long executeTime = (System.currentTimeMillis() - startTime);
                logger.info("url:{}, method:{} ,耗时:{}ms", url, method, executeTime);
            }
        }));
    }


    /**
     * String转map
     *
     * @param str
     * @return
     */
    public static Map<String, String> getStringToMap(String str) {
        //根据逗号截取字符串数组
        String[] str1 = str.split("&");
        //创建Map对象
        Map<String, String> map = new HashMap<String, String>();
        //循环加入map集合
        for (int i = 0; i < str1.length; i++) {
            //根据":"截取字符串数组
            String[] str2 = str1[i].split("=");
            if (str2.length <= 1) {
                map.put(str2[0], "");
            } else {
                //str2[0]为KEY,str2[1]为值
                map.put(str2[0], str2[1]);
            }
        }
        return map;
    }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值