Spring Cloud Zuul 综合使用

Zuul:Pre和Post过滤器

目前我们项目的架构图:

从上图中可以看到,Zuul是我们整个系统的入口。当我们有参数校验的需求时,我们就可以利用Zuul的Pre过滤器,进行参数的校验。例如我现在希望请求都一律带上token参数,否则拒绝请求。在项目中创建一个filter包,在该包中新建一个TokenFilter劳累并继承ZuulFilter,代码如下:

package org.zero.springcloud.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * @program: api-gateway
 * @description: token过滤器
 * @author: 01
 * @create: 2018-08-25 17:03
 **/
@Component
public class TokenFilter extends ZuulFilter {

    @Override
    public String filterType() {
        // 声明过滤器的类型为Pre
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 将这个过滤器的优先级放在 PRE_DECORATION_FILTER_ORDER 之前,数字越小优先级越高
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        // 开启这个过滤器
        return true;
    }

    /**
     * 这个方法用于自定义过滤器的处理代码
     *
     * @return Object
     * @throws ZuulException ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 从上下文中拿到请求对象
        HttpServletRequest request = requestContext.getRequest();

        // 拿出参数里的token
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            // 验证失败
            requestContext.setSendZuulResponse(false);
            // 返回401权限不通过
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }

        return null;
    }
}

重启项目,我们来访问一个接口,不带上token参数,看看是否会返回401。如下:

带上token参数再测试一下,请求成功:

从以上的示例中,可以看到利用Pre可以对请求进行一些预处理。如果希望在请求处理完成后,对返回的数据进行处理的话。就需要使用的Post过滤器,例如我们要在http返回头中,加上一个自定义的X-Foo属性。代码如下:

package org.zero.springcloud.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * @program: api-gateway
 * @description:
 * @author: 01
 * @create: 2018-08-25 17:10
 **/
@Component
public class AddResponseHeaderFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletResponse response = requestContext.getResponse();
        response.setHeader("X-Foo", UUID.randomUUID().toString());

        return null;
    }
}

重启项目,同样访问之前那个接口,测试结果如下:


Zuul:限流

Zuul充当API网关的角色,所有的请求都经过它,所以很适合在其之上对API做限流保护,防止网络×××。需要注意的是,用于限流的过滤器应该在请求被转发之前调用,常见的限流算法有计数器、漏铜和令×××桶算法。

令×××桶算法示意图:

Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令×××桶算法(Token Bucket)来完成限流,非常易于使用。RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率,它支持两种获取permits接口,一种是如果拿不到立刻返回false,一种会阻塞等待一段时间看能不能拿到。

我们来创建一个过滤器,简单使用一下这个RateLimiter。代码如下:

package org.zero.springcloud.apigateway.filter;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.zero.springcloud.apigateway.exception.RateLimiterException;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;

/**
 * @program: api-gateway
 * @description: 限流过滤器
 * @author: 01
 * @create: 2018-08-25 21:04
 **/
@Component
public class RateLimiterFilter extends ZuulFilter {

    /**
     * 每秒钟放入100个令×××
     */
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);

    @Override
    public String filterType() {
        // 限流肯定是得在Pre类型的过滤器里做
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 设置过滤器的优先级为最高
        return SERVLET_DETECTION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        // 尝试从令×××桶中获取令×××
        if (!RATE_LIMITER.tryAcquire()) {
            // 获取失败抛出异常,或做其他处理
            throw new RateLimiterException();
        }

        return null;
    }
}

除了这个RateLimiter之外,GitHub上也有一些开源的实现。我这里发现了一个还不错的,地址如下:

https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit


Zuul:完成权限校验

以上我们演示了pre、post过滤器的简单使用,以及在Zuul上做限流,接下来我们看看如何通过Zuul实现鉴权。通常来说,我们鉴权的对象往往都是用户,我这里已经事先准备好了用户服务以及相关接口。

需求,利用Zuul实现如下功能:

/**
 * /buyer/order/create 只能买家访问 (cookie里有openid)
 * /buyer/order/finish 只能卖家访问 (cookie里有token,并且redis存储了session数据)
 * /buyer/product/list 都可以访问
 */

因为判断用户角色权限的时候,需要通过cookie和redis里缓存的数据进行判断,所以修改配置文件如下:

将之前做实验的所有过滤器都注释掉,然后新建一个AuthBuyerFilter过滤器,用于拦截订单创建的请求。代码如下:

package org.zero.springcloud.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.zero.springcloud.apigateway.utils.CookieUtil;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

/**
 * @program: api-gateway
 * @description: 买家权限过滤器
 * @author: 01
 * @create: 2018-08-25 17:03
 **/
@Component
public class AuthBuyerFilter extends ZuulFilter {

    private static final String ORDER_CREATE = "/order/buyer/order/create";

    @Override
    public String filterType() {
        // 声明过滤器的类型为Pre
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 将这个过滤器的优先级放在 PRE_DECORATION_FILTER_ORDER 之前
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 从上下文中拿到请求对象
        HttpServletRequest request = requestContext.getRequest();

        // 如果访问的是 ORDER_CREATE 则进行拦截,否则不进行拦截
        return ORDER_CREATE.equals(request.getRequestURI());
    }

    /**
     * 这个方法用于自定义过滤器的处理代码
     *
     * @return Object
     * @throws ZuulException ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 从上下文中拿到请求对象
        HttpServletRequest request = requestContext.getRequest();

        // /buyer/order/create 只能买家访问 (cookie里有openid)
        Cookie cookie = CookieUtil.get(request, "openid");
        if (cookie == null || StringUtils.isBlank(cookie.getValue())) {
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }

        return null;
    }
}

接着再新建一个AuthSellerFilter过滤器,用于拦截订单完结的请求。代码如下:

package org.zero.springcloud.apigateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.zero.springcloud.apigateway.constant.RedisConstant;
import org.zero.springcloud.apigateway.utils.CookieUtil;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

/**
 * @program: api-gateway
 * @description: 卖家权限过滤器
 * @author: 01
 * @create: 2018-08-25 17:03
 **/
@Component
public class AuthSellerFilter extends ZuulFilter {

    private final StringRedisTemplate redisTemplate;
    private static final String ORDER_FINISH = "/order/buyer/order/finish";

    @Autowired
    public AuthSellerFilter(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public String filterType() {
        // 声明过滤器的类型为Pre
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 将这个过滤器的优先级放在 PRE_DECORATION_FILTER_ORDER 之前
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 从上下文中拿到请求对象
        HttpServletRequest request = requestContext.getRequest();

        // 如果访问的是 ORDER_FINISH 则进行拦截,否则不进行拦截
        return ORDER_FINISH.equals(request.getRequestURI());
    }

    /**
     * 这个方法用于自定义过滤器的处理代码
     *
     * @return Object
     * @throws ZuulException ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 从上下文中拿到请求对象
        HttpServletRequest request = requestContext.getRequest();

        // /buyer/order/finish 只能卖家访问 (cookie里有token,并且redis存储了session数据)
        if (ORDER_FINISH.equals(request.getRequestURI())) {
            Cookie cookie = CookieUtil.get(request, "token");
            if (cookie == null ||
                    StringUtils.isBlank(cookie.getValue()) ||
                    StringUtils.isNotBlank(redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue())))) {
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            }
        }

        return null;
    }
}

额外话题:

  • 网关不要连接任何服务的关系型数据库
  • 获取数据应该通过调用服务接口的方式进行获取
  • 经常需要获取的数据有必要缓存到redis中,例如需要进行简单的权限缓存

Zuul:跨域

现在我们的项目基本都是前后端分离的,前端通过ajax来请求后端接口。由于浏览器的同源策略,所以会出现跨域的问题。而在微服务架构中,我们可以在网关上统一解决跨域的问题。

在Zuul里增加CorsFilter过滤器的配置类即可。代码如下:

package org.zero.springcloud.apigateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @program: api-gateway
 * @description: 跨域配置
 * @author: 01
 * @create: 2018-08-27 23:02
 **/
@Configuration
public class CorsConfig {

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许cookie跨域
        corsConfiguration.setAllowCredentials(true);
        // 允许任何域名使用
        corsConfiguration.addAllowedOrigin("*");
        // 允许任何头
        corsConfiguration.addAllowedHeader("*");
        // 允许任何方法(post、get等)
        corsConfiguration.addAllowedMethod("*");
        // 设置跨域缓存时间,单位为秒
        corsConfiguration.setMaxAge(300L);

        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对接口配置跨域设置
        source.registerCorsConfiguration("/**", buildConfig());

        return new CorsFilter(source);
    }
}

原文地址:http://blog.51cto.com/zero01/2173360

时间: 2024-07-31 13:27:02

Spring Cloud Zuul 综合使用的相关文章

笔记:Spring Cloud Zuul 快速入门

Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了所有其他微服务的实例信息,这样的设计非常巧妙的将服务治理体系中维护的实例信息利用起来,使得维护服务实例的工作交给了服务治理框架自动完成,而对路由规则的维护,默认会将通过以服务名作为 ContextPath 的方式来创建路由映射,也可以做一些特别的配置,对于签名校验.登录校验等在微服务架构中的冗余问题

spring cloud zuul filter 设置过滤请求

利用spring cloud zuul 设置spring boot 中的filter ,出现了跨域问题,然后根据设置response可解决跨域,同时完成过滤请求 ********************************************************************************************************************* //主要执行的类public class PreRequestLogFilter extends Zu

spring cloud zuul

spring cloud zuul官网:http://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.1.RELEASE/,具体配置不记 spring cloud zuul是配置路由的,我们配置的时候,喜欢这么配置例如: zuul: routes: api-a: path: /api-a/** serviceId: service-A 但是其实我们不配置也是可以路由的,如果我们的api-gateway网关的开的端口是555

Spring Cloud Zuul 实现路由,获取参数

Spring Cloud Zuul作为网关,可实现请求验证过滤,路由到具体服务的作用. 举例: 比如请求订单接口,报文包括验证信息及具体请求参数. 通过配置路由到具体接口,通过参数调用接口,返回具体报文. 参数传递: 1.post请求,String类型json 2.接口获取@ResponseBody,将获取到String转json,再获取详细参数信息 验证:继承ZuulFilter类,重写run方法,调用具体验证服务,验证通过返回true.

Spring Cloud Zuul 添加 ZuulFilter

紧接着上篇随笔Spring Cloud Zuul写,添加过滤器,进行权限验证 1.添加过滤器 package com.dzpykj.filter; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; import com.netflix.zuul.ZuulFilter; import com.netfli

Spring Cloud Zuul性能调整

Spring Cloud 版本: Dalston.SR5 这两天通过JMeter测了一下Spring Cloud Zuul的性能,用的是两台虚机8核8G和4核8G,宿主机是10核逻辑20核,代理的服务简单的返回字符串hello,vm堆内存1G够用 先说一下测试情况,值得一提的是测试并不严谨,因为用的是虚机,并且虚机上还跑了一些其它的东西,所以不能作为最终指导,仅供参考. 8核心的情况下: zuul的性能约是nginx(8个worker)的75%, nginx 8个worker 的cup总占用率为

Spring Cloud Zuul 快速入门

服务网关和Zuul 为什么要有服务网关: 我们都知道在微服务架构中,系统会被拆分为很多个微服务.那么作为客户端要如何去调用这么多的微服务呢?难道要一个个的去调用吗?很显然这是不太实际的,我们需要有一个统一的接口与这些微服务打交道,这就是我们需要服务网关的原因. 我们已经知道,在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求.比如:用户查看一个商品的信息,它可能包含商品基本信息.价格信息.评论信息.折扣信息

spring cloud zuul参数调优

zuul 内置参数 zuul.host.maxTotalConnections 适用于ApacheHttpClient,如果是okhttp无效.每个服务的http客户端连接池最大连接,默认是200. zuul.host.maxPerRouteConnections 适用于ApacheHttpClient,如果是okhttp无效.每个route可用的最大连接数,默认值是20. zuul.semaphore.max-semaphores Hystrix最大的并发请求execution.isolati

Spring Cloud Zuul中使用Swagger汇总API接口文档

有很多读者问过这样的一个问题:虽然使用Swagger可以为Spring MVC编写的接口生成了API文档,但是在微服务化之后,这些API文档都离散在各个微服务中,是否有办法将这些接口都整合到一个文档中? 如果您还不了解Spring Cloud Zuul和Swagger,建议优先阅读下面两篇,有一个初步的了解: Spring Cloud构建微服务架构:服务网关(基础) Spring Boot中使用Swagger2构建强大的RESTful API文档 准备工作 上面说了问题的场景是在微服务化之后,所