详解SpringCloud Zuul过滤器返回值拦截

网友投稿 427 2023-01-29


详解SpringCloud Zuul过滤器返回值拦截

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 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,通过授权服务进行tohttp://ken加密,这里加密方式使用的是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.getCurrentConnSomktext().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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:共享文件系统如何删除密码(怎么删除共享密码)
下一篇:spring 如何将配置信息注入静态变量的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~