獨立使用zuul閘道器分發不同服務的請求、許可權控制,非SpringCloud
閘道器api Gateway的重要性不言而喻,閘道器負責統一接收所有請求,然後根據不同的規則進行轉發到不同的服務。使用閘道器能夠統一的管理請求日誌、進行許可權控制、過濾等,這樣就能避免在每個單體應用中做重複的工作。
這一篇主要是講zuul的獨立使用,就是隻作為一個獨立的專案進行請求轉發,而不關聯SpringCloud的那一堆Eureka、Ribbon等,因為很多時候我們的專案並不都是基於springcloud的微服務,或者不想搞那麼麻煩用註冊中心什麼的,就只想做個簡單的請求轉發代理和許可權控制。
zuul是可以進行開發的,裡面可以自定義一些自己的規則,譬如涉及查表之類的,能夠完成顆粒很細的需求。
這裡我們打算完成如下的功能,當訪問ip/user時就進入到User的獨立專案中,訪問ip/club時就進入到club的獨立專案中。入口是zuul,在zuul裡做許可權控制,譬如查表過濾黑名單、限制同一個userId單位時間內的訪問次數等。
請求轉發
使用zuul很簡單,新建一個Springboot專案,建立時勾選zuul即可。pom.xml如下
然後在Application類加上@EnableZuulProxy註解即可,該專案就具備了zuul的功能了<?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>
@EnableZuulProxy
@SpringBootApplication
public class TestzuulApplication {
public static void main(String[] args) {
SpringApplication.run(TestzuulApplication.class, args);
}
}
然後關鍵的地方在application.yml配置裡,規則在這裡配置主要就是配置zuul.routes相關,api-1名字隨便起,主要是配置裡面的path和url這個是固定的server: port: 9000 zuul: routes: api-1: path: /user/** url: http://localhost:8081/ api-2: path: /club/** url: http://localhost:8082/
通過原始碼可以看到支援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的配置。