SpringCloud Zuul过滤器返回值拦截

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


本文中的代码已提交至: https://gitee.com/cmlbeliever/springcloud 欢迎Star
实现类在:api-getway工程下的com.cml.springcloud.api.filter.AuthResponseFilter


举个例子,用户服务提供一个登录接口,用户名密码正确后返回一个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


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
在 Spring Cloud 中,可以使用 Zuul 作为 API 网关,通过 Zuul 进行请求转发和路由。而 Zuul 还提供了过滤器(Filter)机制,可以在请求被路由到目标服务前、后进行一些预处理或后处理。 在 Zuul 中,过滤器分为四种类型: 1. Pre:在请求被路由到目标服务前执行; 2. Post:在请求被路由到目标服务后执行; 3. Route:用于将请求路由到目标服务的过程中执行; 4. Error:在请求发生错误时执行。 通过实现 Zuul过滤器接口,可以自定义过滤器,并指定过滤器的类型和执行顺序。在过滤器中,可以对请求或响应进行修改或者拦截。 对于过滤器返回值拦截,可以在 Pre 和 Error 类型的过滤器中进行。在 Pre 类型的过滤器中,可以通过抛出异常的方式终止请求,并将异常信息返回给客户端。在 Error 类型的过滤器中,可以通过设置响应状态码、响应头信息等方式,修改响应内容。 例如,在 Pre 类型的过滤器中,可以通过以下代码实现返回拦截: ```java public class AuthFilter extends ZuulFilter { @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String authToken = request.getHeader("Authorization"); if (StringUtils.isBlank(authToken)) { ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); ctx.setResponseBody("Unauthorized"); return null; } return null; } } ``` 在该过滤器中,如果请求头中不存在 Authorization 字段,则设置返回状态码为 401,并设置响应内容为 "Unauthorized",从而实现了返回值拦截
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值