Spring Cloud 系列之 Netflix Zuul 服务网关(二)
< 返回列表时间: 2020-04-17来源:OSCHINA
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
本篇文章为系列文章,未读第一集的同学请猛戳这里: Spring Cloud 系列之 Netflix Zuul 服务网关(一)
本篇文章讲解 Zuul 网关过滤器实现统一鉴权以及网关过滤器异常统一处理。
  
网关过滤器
  
  点击链接观看: 网关过滤器视频 (获取更多请关注公众号「哈喽沃德先生」)
  

  Zuul 包含了对请求的路由和过滤两个核心功能,其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。然而实际上,路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。
  路由映射主要通过 pre 类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则是由 routing 类型的过滤器来完成,对 pre 类型过滤器获得的路由地址进行转发。所以说,过滤器可以说是 Zuul 实现 API 网关功能最核心的部件,每一个进入 Zuul 的 http 请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。
  
关键名词
   类型 :定义路由流程中应用过滤器的阶段。共 pre、routing、post、error 4 个类型。 执行顺序 :在 同类型 中,定义过滤器执行的顺序。比如多个 pre 类型的执行顺序。 条件 :执行过滤器所需的条件。true 开启,false 关闭。 动作 :如果符合条件,将执行的动作。具体操作。
  
过滤器类型
   pre:请求被路由到源服务器之前执行的过滤器 身份认证 选路由 请求日志 routing:处理将请求发送到源服务器的过滤器 post:响应从源服务器返回时执行的过滤器 对响应增加 HTTP 头 收集统计和度量指标 将响应以流的方式发送回客户端 error:上述阶段中出现错误时执行的过滤器
  
入门案例
  
创建过滤器
  
  Spring Cloud Netflix Zuul 中实现过滤器必须包含 4 个基本特征:过滤器类型,执行顺序,执行条件,动作(具体操作)。这些步骤都是 ZuulFilter 接口中定义的 4 个抽象方法: package com.example.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * 网关过滤器 */ @Component public class CustomFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(CustomFilter.class); /** * 过滤器类型 * pre * routing * post * error * * @return */ @Override public String filterType() { return "pre"; } /** * 执行顺序 * 数值越小,优先级越高 * * @return */ @Override public int filterOrder() { return 0; } /** * 执行条件 * true 开启 * false 关闭 * * @return */ @Override public boolean shouldFilter() { return true; } /** * 动作(具体操作) * 具体逻辑 * * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { // 获取请求上下文 RequestContext rc = RequestContext.getCurrentContext(); HttpServletRequest request = rc.getRequest(); logger.info("CustomFilter...method={}, url={}", request.getMethod(), request.getRequestURL().toString()); return null; } } filterType :该函数需要返回一个字符串代表过滤器的类型,而这个类型就是在 http 请求过程中定义的各个阶段。在 Zuul 中默认定义了 4 个不同的生命周期过程类型,具体如下: pre:请求被路由之前调用 routing: 路由请求时被调用 post: routing 和 error 过滤器之后被调用 error:处理请求时发生错误时被调用 filterOrder :通过 int 值来定义过滤器的执行顺序,数值越小优先级越高。 shouldFilter :返回一个 boolean 值来判断该过滤器是否要执行。 run :过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
  
访问
  
  访问: http://localhost:9000/product-service/product/1 控制台输出如下: CustomFilter...method=GET, url=http://localhost:9000/product-service/product/1
  
统一鉴权
  
  接下来我们在网关过滤器中通过 token 判断用户是否登录,完成一个统一鉴权案例。
  
创建过滤器
   package com.example.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.PrintWriter; /** * 权限验证过滤器 */ @Component public class AccessFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { // 获取请求上下文 RequestContext rc = RequestContext.getCurrentContext(); HttpServletRequest request = rc.getRequest(); // 获取表单中的 token String token = request.getParameter("token"); // 业务逻辑处理 if (null == token) { logger.warn("token is null..."); // 请求结束,不在继续向下请求。 rc.setSendZuulResponse(false); // 响应状态码,HTTP 401 错误代表用户没有访问权限 rc.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); // 响应类型 rc.getResponse().setContentType("application/json; charset=utf-8"); PrintWriter writer = null; try { writer = rc.getResponse().getWriter(); // 响应内容 writer.print("{"message":"" + HttpStatus.UNAUTHORIZED.getReasonPhrase() + ""}"); } catch (IOException e) { e.printStackTrace(); } finally { if (null != writer) writer.close(); } } else { // 使用 token 进行身份验证 logger.info("token is OK!"); } return null; } }
  
访问
  
  访问: http://localhost:9000/product-service/product/1 结果如下:

  
  访问: http://localhost:9000/product-service/product/1?token=abc123 结果如下:

  
Zuul 请求的生命周期
  
HTTP 发送请求到 Zuul 网关 Zuul 网关首先经过 pre filter 验证通过后进入 routing filter,接着将请求转发给远程服务,远程服务执行完返回结果,如果出错,则执行 error filter 继续往下执行 post filter 最后返回响应给 HTTP 客户端
  
网关过滤器异常统一处理
  
  点击链接观看: 网关过滤器异常统一处理视频 (获取更多请关注公众号「哈喽沃德先生」)
  
创建过滤器
   package com.example.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.io.IOException; import java.io.PrintWriter; /** * 异常过滤器 */ @Component public class ErrorFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(ErrorFilter.class); @Override public String filterType() { return "error"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext rc = RequestContext.getCurrentContext(); Throwable throwable = rc.getThrowable(); logger.error("ErrorFilter..." + throwable.getCause().getMessage(), throwable); // 响应状态码,HTTP 500 服务器错误 rc.setResponseStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 响应类型 rc.getResponse().setContentType("application/json; charset=utf-8"); PrintWriter writer = null; try { writer = rc.getResponse().getWriter(); // 响应内容 writer.print("{"message":"" + HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase() + ""}"); } catch (IOException e) { e.printStackTrace(); } finally { if (null != writer) writer.close(); } return null; } }
  
模拟异常
  
  在 pre 过滤器中添加模拟异常代码。 // 模拟异常 Integer.parseInt("zuul");
  
配置文件
  
  禁用 Zuul 默认的异常处理 filter: SendErrorFilter zuul: # 禁用 Zuul 默认的异常处理 filter SendErrorFilter: error: disable: true
  
访问
  
  访问: http://localhost:9000/product-service/product/1 结果如下:
下一篇我们讲解 Zuul 和 Hystrix 的无缝结合,实现网关监控、网关熔断、网关限流、网关调优,记得关注噢~

本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议 。
大家可以通过 分类 查看更多关于 Spring Cloud 的文章。
  
🤗 您的 点赞 和 转发 是对我最大的支持。
📢 扫码关注 哈喽沃德先生 「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~
热门排行