可以看到所有的请求都会经过他,那么我们现在要对这个请求做一个权限的校验,加入没有Zuul这个服务,
那你Server A/B/C,都要校验一次,这个每个服务都要做一次,所以权限校验我们可以放到Zuul统一的实现掉,
接下来我们演示如何对请求进行统一的校验,我们现在来做这么一件事,希望所有经过Zuul的请求,都要有一个
参数叫做Token,并且内容不能为空,如果你不带这个参数的话,如果不带这个参数的话,那么就权限校验不通过,
我们新建一个包叫filter,下面新建一个TokenFilter
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class TokenFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//这里从url参数里获取, 也可以从cookie, header里获取
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
/**
* {@code 401 Unauthorized}.
* @see <a href="http://tools.ietf.org/html/rfc7235#section-3.1">HTTP/1.1: Authentication, section 3.1</a>
*/
UNAUTHORIZED(401, "Unauthorized"),
继承ZuulFilter
com.netflix.zuul.ZuulFilter
这里要实现几个方法,实现这四个方法,这里有一个常量类FilterConstants,这里我们要做的是参数校验,用这个PRE_TYPE
@Override
public String filterType() {
return PRE_TYPE;
}
/**
* {@link ZuulFilter#filterType()} pre type.
*/
String PRE_TYPE = "pre";
这个filterOrder这是顺序,这个顺序用什么顺序呢,正如我们之前所说,一种过滤器一个类型下面,有很多个过滤器,
这些都是pre的,这个ORDER我们填什么呢,我们把顺序放到这个过滤器之前,这些顺序越小的越靠前,越小的优先级越高,
放到他前面我们减1就好了
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
/**
* Filter Order for {@link org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter}
*/
int PRE_DECORATION_FILTER_ORDER = 5;
这也是官方推荐的一个写法,
@Override
public boolean shouldFilter() {
return true;
}
这里就是我们的逻辑
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//这里从url参数里获取, 也可以从cookie, header里获取
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
我们从一个对象里面拿到一个请求的内容,叫做RequestContext,这也是Zuul他自己封装的,获取当前的一个上下文,
RequestContext requestContext = RequestContext.getCurrentContext();
在从这里面拿到Request,拿到Request之后大家就熟了,从这我们就可以拿到Token了,这里写点注释,这里我们是从参数里面去拿,
你也可以从Cookie或者header里面获取,基本上也就放到这里面了,获取到了之后判断一下,如果他是空的话呢,这个时候返回没有
权限,往requestContext这里面设置一些值就好了,首先设置sendZuulResponse为false,false是不通过,再设置一个HTTP的状态码,
状态码权限不足我们一般是用401,我们也可以直接写枚举HttpStatus.UNAUTHORIZED.value()
BAD_REQUEST(400, "Bad Request"),
/**
* {@code 401 Unauthorized}.
* @see <a href="http://tools.ietf.org/html/rfc7235#section-3.1">HTTP/1.1: Authentication, section 3.1</a>
*/
UNAUTHORIZED(401, "Unauthorized"),
org.springframework.http.HttpStatus.UNAUTHORIZED
http://localhost:8040/myProduct/product/list?token=1234
现在地址带了token,没问题和之前一样
http://localhost:8040/myProduct/product/list
如果不带TOKEN就401,返回的是401,权限没有通过,会走这个校验的逻辑,你的业务类型可能也不只是判断是否为空,
你拿到这个值之后也可以从数据库里面去读,从Redis里面去比较,都是可以的,我们也可以自定义一个POST Filter,
就是请求到了目标服务,返回了结果之后,对结果进行加工,我们从返回的头里面写一些东西吧
@Component
public class AddResponseHeaderFilter extends ZuulFilter{
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
同样的继承ZuulFilter,type是POST_TYPE
/**
* {@link ZuulFilter#filterType()} post type.
*/
String POST_TYPE = "post";
org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE
filterOrder这个也是比较有讲究的,我们再去看一下order,如果我们自定义POST order的话,
我们会把它放到这个之前,也就是放到这个fiter之前,所以我们选中这个再减1
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER - 1;
}
/**
* Filter Order for {@link org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter#filterOrder()}
*/
int SEND_RESPONSE_FILTER_ORDER = 1000;
org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER
优先级在他之前了,也是获取requestContext
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
不是request,而是要拿到结果了,我们从返回的结果里面写东西了,设置一个header,header名随便设置一个,
生成一下随机数,这就写完了
http://localhost:8040/myProduct/product/list?token=1234
结果是返回了,返回头里面有没有我们添加进去的信息呢,Response Header有吧
以上就是自定义POST Filter的内容
<?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.learn.cloud</groupId>
<artifactId>api-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>cn.learn</groupId>
<artifactId>microcloud02</artifactId>
<version>0.0.1</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<!-- 这个插件,可以将应用打包成一个可执行的jar包 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.learn.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class APIGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(APIGatewayApplication.class, args);
}
}
package com.learn.cloud.filter;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class TokenFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//这里从url参数里获取, 也可以从cookie, header里获取
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
package com.learn.cloud.filter;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
@Component
public class AddResponseHeaderFilter extends ZuulFilter{
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
server.port=8040
spring.application.name=api-gateway
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
eureka.client.serviceUrl.defaultZone=http://admin:1234@10.40.8.152:8761/eureka
#eureka.instance.appname=api-gateway
#zuul.routes.user-route.service-id=product
#zuul.ignoredServices=microservice-consumer-movie-ribbon-hystrix
#zuul.routes.user-route.path=/user/**
#zuul.routes.user-route.url=http://localhost:7900/
#ribbon.eureka.enabled=false
#microservice-simple-provider-user.ribbon.listOfServers=http://localhost:7900/,http://localhost:7901/
zuul.routes.myProduct.path=/myProduct/**
zuul.routes.myProduct.service-id=product
zuul.routes.myProduct.sensitiveHeaders=
#zuul.routes.product=/myProduct/**
management.security.enabled=false