详解SpringCloud Zuul过滤器返回值拦截
作者:cmlbeliever 发布时间:2023-02-05 07:59:37
Zuul作为网关服务,是其他各服务对外中转站,通过Zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加密信息等等。
举个例子,用户服务提供一个登录接口,用户名密码正确后返回一个Token,此Token作为用户服务的通行证,那么用户登录成功后返回的Token就需要进行加密或者防止篡改处理。在到达用户服务其他接口前,就需要对Token进行校验,非法的Token就不需要转发到用户服务中了,直接在网关层返回信息即可。
要修改服务返回的信息,需要使用的是Zuul的过滤器。使用时只需要继承ZuulFilter,实现必要的方法即可。
Zuul提供默认的四种过滤器类型,通过filterType方法进行标识
pre:可以在请求被路由之前调用
route:在路由请求时候被调用
post:在route和error过滤器之后被调用
error:处理请求时发生错误时被调用
过滤器执行的顺序是通过filterOrder方法进行排序,越小的值越优先处理。FilterConstants定义了一些列默认的过滤器的执行顺序和路由类型,大部分需要用到的常量都在这儿。
例子中说明的,只有登录接口需要拦截,所以只需要拦截登录请求(/user/login)即可。可以通过过滤器的shouldFilter方法进行判断是否需要拦截。
由于是在准发用户服务成功后进行的数据修改,所以 * 的类型时post类型的。整个类的实现如下:
public class AuthResponseFilter extends AbstractZuulFilter {
private static final String RESPONSE_KEY_TOKEN = "token";
@Value("${system.config.authFilter.authUrl}")
private String authUrl;
@Value("${system.config.authFilter.tokenKey}")
private String tokenKey = RESPONSE_KEY_TOKEN;
@Autowired
private AuthApi authApi;
@Override
public boolean shouldFilter() {
RequestContext context = getCurrentContext();
return StringUtils.equals(context.getRequest().getRequestURI().toString(), authUrl);
}
@Override
public Object run() {
try {
RequestContext context = getCurrentContext();
InputStream stream = context.getResponseDataStream();
String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
if (StringUtils.isNotBlank(body)) {
Gson gson = new Gson();
@SuppressWarnings("unchecked")
Map<String, String> result = gson.fromJson(body, Map.class);
if (StringUtils.isNotBlank(result.get(tokenKey))) {
AuthModel authResult = authApi.encodeToken(result.get(tokenKey));
if (authResult.getStatus() != HttpServletResponse.SC_OK) {
throw new IllegalArgumentException(authResult.getErrMsg());
}
String accessToken = authResult.getToken();
result.put(tokenKey, accessToken);
}
body = gson.toJson(result);
}
context.setResponseBody(body);
} catch (IOException e) {
rethrowRuntimeException(e);
}
return null;
}
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 2;
}
}
配置文件,中添加授权url和返回token的key:
system.config.authFilter.authUrl=/user/login
system.config.authFilter.tokenKey=token
context.setResponseBody(body);这段代码是核心,通过此方法修改返回数据。
当用户登录成功后,根据返回的token,通过授权服务进行token加密,这里加密方式使用的是JWT。防止用户篡改信息,非法的请求直接可以拦截在网关层。
关于Zuul过滤器的执行过程,这里不需要多说明,源码一看便知,ZuulServletFilter:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
try {
preRouting();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
try {
postRouting();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
方法说明:
preRoute:执行pre类型的过滤器
postRoute:执行post类型的过滤器
route:执行route类型的过滤器
error:执行error类型的过滤器
通过context.setSendZuulResponse(false)可以终止请求的转发,但是只在pre类型的过滤器中设置才可以。
关于如何终止过滤器:
只有pre类型的过滤器支持终止转发,其他过滤器都是按照顺序执行的,而且pre类型的过滤器也只有在所有pre过滤器执行完后才可以终止转发,做不到终止过滤器继续执行。看ZuulServletFilter源码代码:
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
本文中的代码已提交至: https://gitee.com/cmlbeliever/springcloud 欢迎Star
实现类在:api-getway工程下的com.cml.springcloud.api.filter.AuthResponseFilter
本地地址:http://xz.jb51.net:81/201806/yuanma/cmlbeliever-springcloud_jb51.rar
来源:https://blog.csdn.net/cml_blog/article/details/78349703
猜你喜欢
- 默认情况下,如果应用以 Android Q 为目标平台,则在访问外部存储设备中的文件时会进入过滤视图。应用可以使用 Context.getE
- 快速排序快速排序是一种比较高效的排序算法,采用“分而治之”的思想,通过多次比较和交换来实现排序,在一
- 1、HashMap HashMap继承抽象类AbstractMap,实现接口Map、Cloneable, Serializable接口。Ha
- 一、什么是JSONJSON(JavaScript Object Notation)是一种基于JavaScript语法子集的开放标准数据交换格
- poi解析Excel文件版本问题解决办法poi解析Excel文件时有两种格式: HSSFWorkbook格式用来解析Excel2003(xl
- 什么是Mapping同样的,我们先讲基本概念,什么是mapping,上节给大家简要的举了一个例子,还有印象吗?mapping是es中一个比较
- 前言在 Java 中,跳转的实现方式有两种:请求转发和请求重定向,但二者是完全不同的,所以我们今天就来盘它。请求转发和请求重定向主要区别,包
- 本文研究的主要是Java ArrayList扩容问题实例详解的相关内容,具体介绍如下。首先我们需要知道ArrayList里面的实质的其实是一
- 在实际的应用中会经常需要将数据导出成excel,导出的方式除原样导出还有分页导出、分页分sheet导出和大数据量导出。对于excel2003
- Spring-boot JMS 发送消息慢的问题解决1、在《ActiveMQ 基于zookeeper的主从(levelDB Master/S
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。spri
- (注意:本文基于JDK1.8)前言增删改查,修改元素,Vector提供了3个方法,包括迭代器中的一个,不过本文只分析Vector自身的两个修
- 1、获取Class对象在 Java API 中,提供了获取 Class 类对象的三种方法:第一种,使用 Class.forName 静态方法
- 1、在POM.xml文件下添加如下代码;注意:version、configuration、executions三个标签是我后来查找添加的,网
- 最近在研究dubbo时,发现了JAVA的SPI特性。SPI的全名为Service Provider Interface,是JDK内置的一种服
- Mybatis入门-基于配置实现单表的增删改查Mybatis简介官网链接:https://mybatis.org/mybatis-3/zh/
- 最近想关闭一个包的日志打印,经过一番研究实际上就一句话的事,一直没成功是因为name写错了。<logger name="pa
- 以前用序列化都是一些方法需要才实现的,后来业务需求要深拷贝才去研究。参阅了别人博客得出一些总结。序列化是为了把Java对象转化为字节序列(字
- Java读取json数据并存入数据库1. pom依赖<dependency> &nbs
- 说道线程,肯定会想到使用 java.lang.Thread.java这个类那么创建线程也主要有2种方式第一种方式:public class