Spring Cloud Zuul(二)过滤功能使用

一 实现访问鉴权的功能

思想

通过实现zuul的过滤器,检查访问是否带有token参数,如果有就进行路由,如果没有,就拒绝访问,返回401的错误

1 继承ZuulFilter的抽象类并实现4个抽象函数,就可以完成上述功能。


public class AccessFilter extends ZuulFilter {

    @Override
    public Object run() {
         
     RequestContext ctx=RequestContext.getCurrentContext();
     HttpServletRequest request=ctx.getRequest();
     Object accessToken= request.getParameter("accessToken");
     if(accessToken==null) {
         System.out.println("accessToken is faile!" ); 
         //不对其进行路由
         ctx.setSendZuulResponse(false);
         ctx.setResponseStatusCode(401);
         return null;
         
     }
     System.out.println("accessToken is ok!" );
        return null;
    }

    @Override
    public boolean shouldFilter() {
     
        return true;
    }

    @Override
    public int filterOrder() {
     
        return  0;
    }

    @Override
    public String filterType() {
     
        return "pre";
    }

}
 

filterType:过滤器类型,pre代表在路由请求之前执行

filterOrder:过滤器执行顺序,数字越小,优先级越高

shouldFilter:该过滤器是否应该被执行

run:执行的具体逻辑

2 实现了自定义过滤器,还需要创建bean才能启动该过滤器,并使用@EnableZuulProxy 注解开启Zuul的API网关服务功能,

@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {

    public static void main(String[] args) {
         SpringApplication.run(ApiGatewayApplication.class, args);
    }

     
 @Bean
 public AccessFilter accessFilter() {
     return new AccessFilter();
 }
}

3 启动该项目,访问http://localhost:5555/api-b-url/hello,控制台打印出accessToken is faile!,访问http://localhost:5555/api-b-url/hello?accessToken=111,就可以正常访问。

 

二 过滤器的生命周期有四个

pre:路由之前被调用

routing:在路由请求的时候被调用

post:在routing和error后被调用

error:处理请求的时候发送错误时被调用

其中代码如下:

try{

   preRoute();

}catch(Exception ex){

error(ex);

postRoute();

return ;

}

try{

   route();

}catch(Exception ex){

error(ex);

postRoute();

return ;

}

try{

   postRoute();

}catch(Exception ex){

error(ex);

return ;

}

 

二  关于异常的处理

1 创建一个pre类型的过滤器,并在run方法中跑出异常,添加@Component注解,让spring可以创建该实例

import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
@Component
public class ThrowExceptionFilter extends ZuulFilter {

    @Override
    public Object run() {
        doSomething();
        return null;
    }

    @Override
    public boolean shouldFilter() {
     
        return true;
    }

    @Override
    public int filterOrder() {
         
        return 0;
    }

    @Override
    public String filterType() {
         
        return "pre";
    }
    
    private void doSomething() {
        throw new RuntimeException("Exist some errors");
    }

}
2 访问http://localhost:5555/api-b-url/hello?accessToken=111 地址,会发现一片空白,啥异常都没有输出,如下图

 

3  上述结果是因为有一个post类型的过滤器中,有一个SendErrorFilter过滤器是用来处理异常的,没有异常说明,这个过滤器没有被执行,该过滤器被执行的条件是请求的上下文必须有error.status_code参数,而我们ThrowExceptionFilter并没有设置这个参数,自然就不会进入该过滤器。把run方法改为如下,再次访问相同的地址,会发现界面有异常信息输出

public Object run() {
        
        System.out.println("ThrowExceptionFilter  run before");
        RequestContext ctx=RequestContext.getCurrentContext();
        try {
             doSomething();    
        }catch(Exception ex) {
            ctx.set("error.status_code",HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            ctx.set("error.exception",ex);
        }
 
        System.out.println("ThrowExceptionFilter  run after");
        return null;
    }

 

 

 

 4 上述方法有个问题,需要try-catch来处理业务,并设置error.status_code的值,有的时候可能因为其他因素,依然会使得一些异常从过滤器中抛出,并没有try-catch来处理,这个时候可以用到error类型的过滤器来处理,定义一个error类型的过滤器,再次使用没有try-catch的ThrowExceptionFilter

@Component
public class ErrorExceptionFilter extends ZuulFilter {

    @Override
    public Object run() {
            RequestContext ctx=RequestContext.getCurrentContext();
            Throwable t=ctx.getThrowable();
            ctx.set("error.status_code",HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            ctx.set("error.exception",t.getCause());
            return null;
    }

    @Override
    public boolean shouldFilter() {
     
        return true;
    }

    @Override
    public int filterOrder() {
         
        return 10;
    }

    @Override
    public String filterType() {
         
        return "error";
    }
    
     

}
访问http://localhost:5555/api-b-url/hello?accessToken=111,界面上会有异常打印出出来。

 

5 上述方法会有不足,因为post阶段调用的过滤器抛出异常后,经过error过滤器处理后,就没其他类型的过滤器来处理了。上述两种方法,都设置了error.status_code的参数,而这些参数起作用的是在post类型的SendErrorFilter过滤器,但是post类型的过滤器报错后,只会执行error类型过滤器,不会执行post类型过滤器,所以页面没有任何输出,将filterType方法的pre改为post。

@Component
public class ThrowExceptionFilter extends ZuulFilter {

    @Override
    public Object run() {
        
        System.out.println("ThrowExceptionFilter  run before");
        RequestContext ctx=RequestContext.getCurrentContext();
         
             doSomething();    
         
 
        System.out.println("ThrowExceptionFilter  run after");
        return null;
    }

    @Override
    public boolean shouldFilter() {
     
        return true;
    }

    @Override
    public int filterOrder() {
         
        return 0;
    }

    @Override
    public String filterType() {
         
        return "post";
    }
    
    private void doSomething() {
        throw new RuntimeException("Exist some errors");
    }

}

访问地址http://localhost:5555/api-b-url/hello?accessToken=111,界面是一片空白

 

6 由于是因为post类型的过滤器发生了异常,执行error类型的过滤器,但是没有执行post类型的SendErrorFilter过滤器,我们只需要在error类型的过滤器里面执行SendErrorFilter过滤器的run方法即可,由于任何类型的过滤器发生异常都会导致执行error类型过滤器,所以在error过滤器里面需要判断是仅是post阶段发生的异常就有效。但是如何知道是哪个阶段发生的异常呢,查看源码发现FilterProcessor的processZuulFilter(ZuulFilter filter),该方法定义了用来执行filter的具体逻辑,需要继承FilterProcessor类,重写该方法,增加异常捕获,如果发生异常,就把过滤器类型字段放到请求的上下文中。

public class DidiFilterProcessor extends FilterProcessor {

    @Override
    public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
       try {
           return super.processZuulFilter(filter);
       }catch(Exception ex) {
           RequestContext ctx=RequestContext.getCurrentContext();
           ctx.set("failed.filter",filter);
           throw ex;
       }
         
    }

}
 

7  继承SendErrorFilter类,仅对于post阶段抛出的异常有效

@Component

public class ErrorExtFilter extends SendErrorFilter {

     
    @Override
    public boolean shouldFilter() {
     
         RequestContext ctx=RequestContext.getCurrentContext();
            System.out.println("ErrorExtFilter  run before");
            ZuulFilter failedFilter=(ZuulFilter)ctx.get("failed.filter");
            if(failedFilter!=null&&failedFilter.filterType().equals("post")  ) {
                Throwable t=ctx.getThrowable();
                ctx.set("error.status_code",HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                ctx.set("error.exception",t.getCause());
                return true;
             }
            return false;
    }

    @Override
    public int filterOrder() {
         
        return 30;
    }

    @Override
    public String filterType() {
         
        return "error";
    }
    
     

}

8 扩展的过滤器处理类还没生效,需要在主类中FilterProcessor.setProcessor(new DidiFilterProcessor());重新设置过滤器处理类。

public class ApiGatewayApplication {

    public static void main(String[] args) {
        FilterProcessor.setProcessor(new DidiFilterProcessor());
        SpringApplication.run(ApiGatewayApplication.class, args);
    }

}

 

 

三 禁用过滤器,可以禁用自定义的过滤器,也可以禁用默认的核心过滤器

zuul.<SimpleClassName>.<filterType>.disable=true

SimpleClassName:类名,比如上述用到的AccessFilter

filterType:过滤器类型,比如pre,post等

zuul:
      AccessFilter:
                         pre: 
                               disable: true

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值