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

时间:2022-09-28 21:28:59

zuul作为网关服务,是其他各服务对外中转站,通过zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加密信息等等。

举个例子,用户服务提供一个登录接口,用户名密码正确后返回一个token,此token作为用户服务的通行证,那么用户登录成功后返回的token就需要进行加密或者防止篡改处理。在到达用户服务其他接口前,就需要对token进行校验,非法的token就不需要转发到用户服务中了,直接在网关层返回信息即可。

要修改服务返回的信息,需要使用的是zuul的过滤器。使用时只需要继承zuulfilter,实现必要的方法即可。

zuul提供默认的四种过滤器类型,通过filtertype方法进行标识

  1. pre:可以在请求被路由之前调用
  2. route:在路由请求时候被调用
  3. post:在route和error过滤器之后被调用
  4. error:处理请求时发生错误时被调用

过滤器执行的顺序是通过filterorder方法进行排序,越小的值越优先处理。filterconstants定义了一些列默认的过滤器的执行顺序和路由类型,大部分需要用到的常量都在这儿。

例子中说明的,只有登录接口需要拦截,所以只需要拦截登录请求(/user/login)即可。可以通过过滤器的shouldfilter方法进行判断是否需要拦截。

由于是在准发用户服务成功后进行的数据修改,所以拦截器的类型时post类型的。整个类的实现如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@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();
  }
 }

方法说明:

  1. preroute:执行pre类型的过滤器
  2. postroute:执行post类型的过滤器
  3. route:执行route类型的过滤器
  4. error:执行error类型的过滤器

通过context.setsendzuulresponse(false)可以终止请求的转发,但是只在pre类型的过滤器中设置才可以。

关于如何终止过滤器:

只有pre类型的过滤器支持终止转发,其他过滤器都是按照顺序执行的,而且pre类型的过滤器也只有在所有pre过滤器执行完后才可以终止转发,做不到终止过滤器继续执行。看zuulservletfilter源码代码:

?
1
2
3
4
5
// 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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/cml_blog/article/details/78349703