• idea创建zuul网关实现过滤器入门实例


    zuul两个重要功能,一个是路由转发,另一个就是过滤器。路由转发上一篇随笔已经写了,这一个写过滤器的实例。

    spring cloud zuul 实现的过滤器需要包含4个基本特征:过滤类型、执行顺序、执行条件、具体操作。也就是zuulFilter接口的4个抽象方法。

    ZuulFilter源码
    
    /*
     * Copyright 2013 Netflix, Inc.
     *
     *      Licensed under the Apache License, Version 2.0 (the "License");
     *      you may not use this file except in compliance with the License.
     *      You may obtain a copy of the License at
     *
     *          http://www.apache.org/licenses/LICENSE-2.0
     *
     *      Unless required by applicable law or agreed to in writing, software
     *      distributed under the License is distributed on an "AS IS" BASIS,
     *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *      See the License for the specific language governing permissions and
     *      limitations under the License.
     */
    package com.netflix.zuul;
    
    import com.netflix.config.DynamicBooleanProperty;
    import com.netflix.config.DynamicPropertyFactory;
    import com.netflix.zuul.monitoring.MonitoringHelper;
    import com.netflix.zuul.monitoring.Tracer;
    import com.netflix.zuul.monitoring.TracerFactory;
    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.concurrent.atomic.AtomicReference;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertSame;
    import static org.mockito.Mockito.*;
    
    /**
     * Base abstract class for ZuulFilters. The base class defines abstract methods to define:
     * filterType() - to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     * <p/>
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     * <p/>
     * ZuulFilters may be disabled using Archius Properties.
     * <p/>
     * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false
     *
     * @author Mikey Cohen
     *         Date: 10/26/11
     *         Time: 4:29 PM
     */
    public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
    
        private final AtomicReference<DynamicBooleanProperty> filterDisabledRef = new AtomicReference<>();
    
        /**
         * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
         * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
         * We also support a "static" type for static responses see  StaticResponseFilter.
         * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
         *
         * @return A String representing that type
         */
        abstract public String filterType();
    
        /**
         * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
         * important for a filter. filterOrders do not need to be sequential.
         *
         * @return the int order of a filter
         */
        abstract public int filterOrder();
    
        /**
         * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false
         *
         * @return true by default
         */
        public boolean isStaticFilter() {
            return true;
        }
    
        /**
         * The name of the Archaius property to disable this filter. by default it is zuul.[classname].[filtertype].disable
         *
         * @return
         */
        public String disablePropertyName() {
            return "zuul." + this.getClass().getSimpleName() + "." + filterType() + ".disable";
        }
    
        /**
         * If true, the filter has been disabled by archaius and will not be run
         *
         * @return
         */
        public boolean isFilterDisabled() {
            filterDisabledRef.compareAndSet(null, DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false));
            return filterDisabledRef.get().get();
        }
    
        /**
         * runFilter checks !isFilterDisabled() and shouldFilter(). The run() method is invoked if both are true.
         *
         * @return the return from ZuulFilterResult
         */
        public ZuulFilterResult runFilter() {
            ZuulFilterResult zr = new ZuulFilterResult();
            if (!isFilterDisabled()) {
                if (shouldFilter()) {
                    Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
                    try {
                        Object res = run();
                        zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                    } catch (Throwable e) {
                        t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                        zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                        zr.setException(e);
                    } finally {
                        t.stopAndLog();
                    }
                } else {
                    zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
                }
            }
            return zr;
        }
    
        public int compareTo(ZuulFilter filter) {
            return Integer.compare(this.filterOrder(), filter.filterOrder());
        }
    
        public static class TestUnit {
    
            static Field field = null;
            static {
                try {
                    field = ZuulFilter.class.getDeclaredField("filterDisabledRef");
                    field.setAccessible(true);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
    
            @Mock
            private ZuulFilter f1;
            @Mock
            private ZuulFilter f2;
    
            @Before
            public void before() {
                MockitoAnnotations.initMocks(this);
                MonitoringHelper.initMocks();
            }
    
            @Test
            public void testSort() {
    
                when(f1.filterOrder()).thenReturn(1);
                when(f2.filterOrder()).thenReturn(10);
                when(f1.compareTo(any(ZuulFilter.class))).thenCallRealMethod();
                when(f2.compareTo(any(ZuulFilter.class))).thenCallRealMethod();
    
                ArrayList<ZuulFilter> list = new ArrayList<ZuulFilter>();
                list.add(f2);
                list.add(f1);
    
                Collections.sort(list);
    
                assertSame(f1, list.get(0));
            }
    
            @Test
            public void testShouldFilter() {
                class TestZuulFilter extends ZuulFilter {
    
                    @Override
                    public String filterType() {
                        return null;
                    }
    
                    @Override
                    public int filterOrder() {
                        return 0;
                    }
    
                    public boolean shouldFilter() {
                        return false;
                    }
    
                    public Object run() {
                        return null;
                    }
                }
    
                TestZuulFilter tf1 = spy(new TestZuulFilter());
                TestZuulFilter tf2 = spy(new TestZuulFilter());
    
                when(tf1.shouldFilter()).thenReturn(true);
                when(tf2.shouldFilter()).thenReturn(false);
    
                try {
                    tf1.runFilter();
                    tf2.runFilter();
                    verify(tf1, times(1)).run();
                    verify(tf2, times(0)).run();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
    
            }
    
            @Test
            public void testIsFilterDisabled() {
                class TestZuulFilter extends ZuulFilter {
    
                    @Override
                    public String filterType() {
                        return null;
                    }
    
                    @Override
                    public int filterOrder() {
                        return 0;
                    }
    
                    public boolean isFilterDisabled() {
                        return false;
                    }
    
                    public boolean shouldFilter() {
                        return true;
                    }
    
                    public Object run() {
                        return null;
                    }
                }
    
                TestZuulFilter tf1 = spy(new TestZuulFilter());
                TestZuulFilter tf2 = spy(new TestZuulFilter());
    
                when(tf1.isFilterDisabled()).thenReturn(false);
                when(tf2.isFilterDisabled()).thenReturn(true);
    
                try {
                    tf1.runFilter();
                    tf2.runFilter();
                    verify(tf1, times(1)).run();
                    verify(tf2, times(0)).run();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
    
            }
    
            @Test
            public void testDisabledPropNameOnInit() throws Exception {
                class TestZuulFilter extends ZuulFilter {
    
                    final String filterType;
    
                    public TestZuulFilter(String filterType) {
                        this.filterType = filterType;
                    }
    
                    @Override
                    public boolean shouldFilter() {
                        return false;
                    }
    
                    @Override
                    public Object run() {
                        return null;
                    }
    
                    @Override
                    public String filterType() {
                        return filterType;
                    }
    
                    @Override
                    public int filterOrder() {
                        return 0;
                    }
                }
    
                TestZuulFilter filter = new TestZuulFilter("pre");
                assertFalse(filter.isFilterDisabled());
    
                @SuppressWarnings("unchecked")
                AtomicReference<DynamicBooleanProperty> filterDisabledRef = (AtomicReference<DynamicBooleanProperty>) field.get(filter);
                String filterName = filterDisabledRef.get().getName();
                assertEquals("zuul.TestZuulFilter.pre.disable", filterName);
            }
    
        }
    }
    

    filterType:这个函数需要返回一个字符串来代表过滤器的类型,而这个类型就是在HTTP请求过程中定义的各个阶段,在zuul中默认定义了4种不同生命周期的过滤器类型。分别是:

    •      pre:   可以在请求被路由之前调用
    •    routing:在路由时被调用
    •   post       :在routing和error过滤器之后被调用
    •   error   : 处理请求发生错误时被调用

    filterOrder: 通过int 值来定义过滤器的执行顺序,数值越小优先级越高

    shouldFilter:返回一个boolean值来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围

    run  :过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后对处理结果进行加工。

    第一步:自定义一个过滤器

    package com.ssc.zuul_demo;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Map;
    import java.util.logging.Logger;
    
    public class AccessFilter extends ZuulFilter {
        Logger logger=Logger.getLogger("AccessFilter");
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
       /**
       *该过滤器主要是看请求是否存在accessToken参数,如果存在是正常访问,否则返回状态码401
       */
        @Override
        public Object run() throws ZuulException {
            RequestContext ctx= RequestContext.getCurrentContext();
            HttpServletRequest request=ctx.getRequest();
            System.out.println(request.getMethod()+"   "+request.getRequestURL().toString());
            Object accessToken =request.getParameter("accessToken");
            if(accessToken==null){
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
            }
            return null;
        }
    }
    

    需要为其创建具体的Bean才能启动该过滤器,否则它并不会直接生效,当然也可以这样改

    package com.ssc.zuul_demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    import org.springframework.context.annotation.Bean;
    
    @EnableZuulProxy
    @SpringBootApplication
    @EnableEurekaClient
    public class ZuulDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulDemoApplication.class, args);
        }
        @Bean
        public AccessFilter accessFilter(){
            return new AccessFilter();
        }
    
    }
    

    启动服务,校验

    验证反例,不加 accessToken参数

    将代码传到gitee:cloud_demo: springcloud样例 (gitee.com)

  • 相关阅读:
    组件化的使用
    MacOS 升级后pod 出现的问题
    协议(Protocol) 和代理(Delegate)
    分类(Category)的本质 及其与类扩展(Extension) /继承(Inherit)的区别
    KVC
    KVO的使用及底层实现
    OC对象的本质及分类
    大端小端
    为什么一个指针在32位系统中占4个字节,在64位系统中占8个字节?
    quarts之Cron表达式示例
  • 原文地址:https://www.cnblogs.com/songlove/p/15834670.html
Copyright © 2020-2023  润新知