1. 程式人生 > >獨立使用zuul閘道器分發不同服務的請求、許可權控制,非SpringCloud

獨立使用zuul閘道器分發不同服務的請求、許可權控制,非SpringCloud

閘道器api Gateway的重要性不言而喻,閘道器負責統一接收所有請求,然後根據不同的規則進行轉發到不同的服務。使用閘道器能夠統一的管理請求日誌、進行許可權控制、過濾等,這樣就能避免在每個單體應用中做重複的工作。

這一篇主要是講zuul的獨立使用,就是隻作為一個獨立的專案進行請求轉發,而不關聯SpringCloud的那一堆Eureka、Ribbon等,因為很多時候我們的專案並不都是基於springcloud的微服務,或者不想搞那麼麻煩用註冊中心什麼的,就只想做個簡單的請求轉發代理和許可權控制。

zuul是可以進行開發的,裡面可以自定義一些自己的規則,譬如涉及查表之類的,能夠完成顆粒很細的需求。

這裡我們打算完成如下的功能,當訪問ip/user時就進入到User的獨立專案中,訪問ip/club時就進入到club的獨立專案中。入口是zuul,在zuul裡做許可權控制,譬如查表過濾黑名單、限制同一個userId單位時間內的訪問次數等。

請求轉發

使用zuul很簡單,新建一個Springboot專案,建立時勾選zuul即可。pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.tianyalei</groupId>
	<artifactId>testzuul</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>testzuul</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR3</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zuul</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>
然後在Application類加上@EnableZuulProxy註解即可,該專案就具備了zuul的功能了
@EnableZuulProxy
@SpringBootApplication
public class TestzuulApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestzuulApplication.class, args);
	}
}
然後關鍵的地方在application.yml配置裡,規則在這裡配置
server:
  port: 9000
zuul:
  routes:
    api-1:
      path: /user/**
      url: http://localhost:8081/
    api-2:
      path: /club/**
      url: http://localhost:8082/
主要就是配置zuul.routes相關,api-1名字隨便起,主要是配置裡面的path和url這個是固定的

通過原始碼可以看到支援serviceId,url兩種,path是固定的,代表訪問zuul時的路徑,url代表訪問該路徑時會被轉發到哪個url上,serviceId是給springcloud用的,代表在Eureka上註冊的服務id,也是確定是轉發到哪一個服務的。

這裡我們配置的是url,當訪問localhost:9000/user/abc時,就相當於訪問localhost:8081/abc.

到這裡就已經完成了請求轉發了,如果你本地跑了兩個專案,埠分別是8081,8082,就已經可以通過請求zuul配置的url規則訪問到了。



@RequestMapping("club")
    public String index() {
        return "hello,我來自Club客戶端";
    }

許可權控制

在zuul裡新建一個AccessFilter類,如下的寫法。
package com.tianyalei.testzuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class AccessFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);

    @Override
    public String filterType() {
        //前置過濾器
        return "pre";
    }

    @Override
    public int filterOrder() {
        //優先順序,數字越大,優先順序越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //是否執行該過濾器,true代表需要過濾
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());

        //獲取傳來的引數accessToken
        Object accessToken = request.getParameter("accessToken");
        if(accessToken == null) {
            log.warn("access token is empty");
            //過濾該請求,不往下級服務去轉發請求,到此結束
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("{\"result\":\"accessToken is empty!\"}");
            return null;
        }
        //如果有token,則進行路由轉發
        log.info("access token ok");
        //這裡return的值沒有意義,zuul框架沒有使用該返回值
        return null;
    }

}

別的先不管,看看run方法,在這裡可以獲取到使用者傳來的所有引數,然後可以配置自己的規則來決定是否往最終的服務轉發請求,false為不給最終的服務傳送這次請求,預設為true。在這裡,我們設定如果accessToken為空,就停止轉發,直接給客戶端返回結果。需要注意一點,Filter是可以配置多個的,按照order從小到大依次執行,即使設定了setSendZuulResponse(false),也是會繼續執行下一個Filter的。

注意,路由轉發的停止和繼續是由ctx.setSendZuulResponse來控制的,與下面的return null無關,這個方法的return值沒有意義,並沒有使用。

效果如圖


下一篇來詳細看看Filter的配置。