Spring Boot Zuul authentication

Do a simple authentication

token authentication, verification token

Usually verify operations such as authentication zuul gateway level, in addition to authentication, it may also be limiting, encryption.

Inheritance ZuulFilter,

ZuulFilter Source: implemented in IZuulFilter

 * Copyright 2013 Netflix, Inc.
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);
        }

    }
}

The key of four methods:

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

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

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

	@Override
	public String filterType() {
		return null;
	}

run (): the specific implementation of the business logic

shouldFilter (): whether the filter, the filter is in effect, true to execute the filter needs to be filtered

filterOrder (): execution priority filter, are higher priority
filterType (): type of filter, there are post, pre, route, error

PRE: This type of filters in Request routing performed prior to the source web-service. For implementing Authentication, select the source service address
ROUTING: the type of filters used in the Request routing to a web-service source, the source is a web-service business logic services. As used herein, HttpClient requested web-service.
POST: The type of the filters is performed ROUTING return Response. Response to achieve the results to be modified, as well as the collection of statistical data will be transmitted Response clients.
ERROR: three processes above any error ERROR types of filters are handed over for processing.
The main concern pre, post and error. Represent the pre-filter, post-filter and filter abnormalities.

More than four in FilterConstants this configuration tool can be found in all classes

TokenAuthFilter:

package com.chwl.cn.token;

import java.util.Objects;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import com.chwl.cn.config.redis.RedisUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;

@Component
public class TokenAuthFilter extends ZuulFilter  {
	
	@Autowired
	private RedisUtils redisUtils;
	
	//排除过滤的 uri 地址
    private static final String LOGIN_URI = "/user/login";
    private static final String REGISTER_URI = "/user/register";
    
    private static final String AUTH_TOKEN = "token";

	@Override
	public Object run() throws ZuulException {
		RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        HttpServletResponse response = requestContext.getResponse();
        String token = request.getHeader(AUTH_TOKEN);
        	Object object = redisUtils.get(token);
        	//token存在,删除原来的token,重新生成token返回给客户端
        	if(!Objects.isNull(object)){
        		redisUtils.del(token);
        		String uuidToken = UUID.randomUUID().toString();
        		redisUtils.set(uuidToken,uuidToken);
        		response.setHeader("Access-Control-Expose-Headers",
        				"Cache-Control,Content-Type,Expires,Pragma,Content-Language,Last-Modified,token");
        		response.setHeader("token", uuidToken); // 设置响应头
        		response.setStatus(HttpStatus.SC_OK);
        		requestContext.setResponseBody("token验证成功!");
        	}else {
        		//不存在,直接返回验证失败,让其重新登录
        		requestContext.setSendZuulResponse(false);//不会被zuul路由转发,也就是不会请求到后端具体的服务。但是如果当前filter后面还有其他filter的话,其他filter依然会执行
//        		requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);//和response.setStatus(HttpStatus.SC_UNAUTHORIZED);一样的效果
        		response.setStatus(HttpStatus.SC_UNAUTHORIZED);//401
        		requestContext.setResponseBody("token验证失败!");
        	}
        	return null;
	}

	@Override
	public boolean shouldFilter() {
		RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        HttpServletResponse response = requestContext.getResponse();
        String requestURI = request.getRequestURI();
        //登录和注册放行
        if(LOGIN_URI.equals(requestURI)||REGISTER_URI.equals(requestURI)){
        	String uuidToken = UUID.randomUUID().toString();
    		redisUtils.set(uuidToken,uuidToken);
    		response.setHeader("Access-Control-Expose-Headers",
    				"Cache-Control,Content-Type,Expires,Pragma,Content-Language,Last-Modified,token");
    		response.setHeader("token", uuidToken); // 设置响应头
        	return false;
        }
		return true;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.SERVLET_DETECTION_FILTER_ORDER-1;
	}

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

}

requestContext.setSendZuulResponse (false); to note here is that this set does not route false to request the back-end server. But if there is, then filter behind the filter performs as usual in this rear filter, so here if the verification is not passed, but also no need to continue the implementation of the other filter. In other shouldFilter filter () method is determined by adding the following to a little better:

@Override
public boolean shouldFilter() {
    RequestContext ctx = RequestContext.getCurrentContext();
 
    if(!ctx.sendZuulResponse()){
        return false;
    }
    return true;
}

 

First into a token string Redis, random, for testing

The return value being given

After a token verification

Try it again does not pass the token

 

401 verification token you have not seen

 

 

 

Published 288 original articles · won praise 88 · views 430 000 +

Guess you like

Origin blog.csdn.net/ypp91zr/article/details/90277413