学习使用zuul的原因是现在原有架构的一些功能被拆分为微服务,每个服务可能都需要调用其他服务,这样就会有服务之间的不断的调用,但是还有一个对外提供的接口需要做一些验证,还有登录等需要做一些验证,这些功能抽出来作为一个统一的管理端,原来的方式将这些功能单独作为一个服务,后来发现zuul有类似的功能,所以学习了一下并在项目中进行了使用,这篇文章主要参考了程序猿DD的文章,并对一些已知的事实进行了验证。最简单的方式,首先引入以下jar包
http://localhost:9001/api-b/consumer请求改为http://localhost:9001/api-b/consumer?accessToken=abc这种URL的方式就可以通过,否则就会出现401,如果要实现过滤的生效还需要在主类中增加以下的代码,让其形成能够运行的bean
<dependencies>
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactld>spring - cloud - starter - zuul</artifactid>
</dependency>
</dependencies>
spring-cloud-starter-zuul包中包含了提供线程保护以及熔断处理的spring-cloud-starter-hystrix依赖,提供监控的spring-boot-star七er-actutor依赖以及提供客户端负载的spring-cloud-starter-ribbon依赖,在主类中配置以下内容:
@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
配置文件中提供以下内容
zuul.routes.api-a-url.path=/api-gateway/**
zuul.routes.api-a-url.url=http://localhost:8083/
这样在请求localhost/api-gateway/api的时候就会转发到http://localhost:8083/api中去,实现对请求的转发,更进一步的是实现zuul和Eureka的结合,在zuul服务中继续添加以下jar包
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-eureka</artifactid>
</dependency>
配置文件添加以下内容,同时将原配置文件中的内容注释掉
spring.application.name = api-gateway
#eureka服务的注册地址
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/
#服务一的转发配置
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=COMPUTE-SERVICE
#服务二的转发配置
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=EUREKA-CONSUMER-RIBBON-HYSTRIX
这样就可以实现多个服务的请求转发,这只是请求转发的过程,请求转发,但是还需要对一些登录的接口以及对外提供服务的接口做一些过滤的功能,那怎么去实现呢?就是利用ZuulFilter,底层是基于javax.servlet.Filter,以下的Java代码就是实现了对请求参数的过滤
public class AccessFilter extends ZuulFilter {
/**
* 过滤器的类型, 它决定过滤器在请求的哪个生命周期中执行。
* 这里定义为pre, 代表会在请求被路由之前执行
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序。
* 当请求在一个阶段中存在多个过滤器时, 需要根据该方法返回的值来依次执行。
* 越小的越先执行
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 判断该过滤器是否需要被执行。
* 这里我们直接返回了true, 因此该过滤器对所有请求都会生效。
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
//zuul不对空的路由进行路由
ctx.setSendZuulResponse(false);
//返回的错误码
ctx.setResponseStatusCode(401);
return "请添加token";
}
return null;
}
}
http://localhost:9001/api-b/consumer请求改为http://localhost:9001/api-b/consumer?accessToken=abc这种URL的方式就可以通过,否则就会出现401,如果要实现过滤的生效还需要在主类中增加以下的代码,让其形成能够运行的bean
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
如果按照请求的类型进行过滤的时候需要添加以下代码
@Override
public boolean shouldFilter() {
String method = RequestContext.getCurrentContext().getRequest().getMethod();
if ("PUT".equals(method) || "POST".equals(method) || "DELETE".equals(method)) {
return true;
}
return false;
}
对于跨域问题的解决可以采用以下方式
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(ORIGIN);
config.addAllowedHeader("Origin");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedHeader("Accept");
config.setMaxAge(3600L);
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}