文章目录
需求
- 在zuul中执行 身份验证
- 拦截请求的结果
- 返回自己想输出的结果
zuul 过滤器类型
pre:可以在请求被路由之前调用
ServletDetectionFilter
Servlet30WrapperFilter
FormBodyWrapperFilter
...
route:在路由请求时候被调用
RibbonRoutingFilter
SimpleHostRoutingFilter
SendForwardFilter
post:在route和error过滤器之后被调用
SendResponseFilter
error:处理请求时发生错误时被调用
执行逻辑分析
1.ZuulServlet
所有的请求都从该类的 service() 方法中开始,该类继承自 HttpServlet
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
// 把request与response对象 存入与线程绑定的 RequestContext 对象中(RequestContext 继承 ConcurrentHashMap),方便后面在各处调用
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
// 执行前置过滤器
try {
this.preRoute();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
// 执行路由过滤器
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
// 执行路由后的过滤器
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
2.FilterProcessor
该类中对 过滤器进行处理
// 传入参数是拦截器的类型
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
// 第一次获取时会初始化过滤器,并根据 过滤器设置的 order 值进行排序,具体逻辑可以自己进去看看。
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for(int i = 0; i < list.size(); ++i) {
ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
// 这里开始执行各个过滤器
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
3.ZuulFilter
该类是一个抽象类,如果我们想要自定义过滤器,继承该类然后加入spring容器即可
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!this.isFilterDisabled()) {
// 这里判断是否需要执行该过滤器,实现类中需要实现该方法
if (this.shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
// 这里执行具体实现类的run方法
Object res = this.run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable var7) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(var7);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
4.具体的过滤器
我罗列我觉得重要的过滤器
4.1RibbonRoutingFilter
该过滤器类型是:route,具体的作用就是 去执行真正的请求
// 这是该类重写的 shouldFilter 方法,这里控制着是否需要执行该过滤器
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
// 可以通过代码RequestContext.getCurrentContext().ctx.setSendZuulResponse(false);来控制不让该过滤器执行
return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
&& ctx.sendZuulResponse());
}
// 该过滤器执行的逻辑
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
// 这里进行真实的请求
ClientHttpResponse response = forward(commandContext);
// 这里执行的是这一句 RequestContext.getCurrentContext().set("zuulResponse", resp); 就是把返回对象存入与线程绑定的对象中
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
4.2SendResponseFilter
该过滤器的类型是:post 看类名也可以理解出是用来处理返回值的
@Override
public Object run() {
try {
// 写入请求头数据
addResponseHeaders();
// 写出返回的数据到浏览器,着重看这里的逻辑
writeResponse();
}
catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
private void writeResponse() throws Exception {
RequestContext context = RequestContext.getCurrentContext();
// there is no body to send
if (context.getResponseBody() == null
&& context.getResponseDataStream() == null) {
return;
}
HttpServletResponse servletResponse = context.getResponse();
if (servletResponse.getCharacterEncoding() == null) { // only set if not set
servletResponse.setCharacterEncoding("UTF-8");
}
OutputStream outStream = servletResponse.getOutputStream();
InputStream is = null;
try {
// 这里可以控制返回的数据,如果我们通过代码 RequestContext.getCurrentContext().setResponseBody(result.toString()); 设置了自定义的返回值,就不会输出 router 过滤器中得到的返回数据
if (context.getResponseBody() != null) {
String body = context.getResponseBody();
is = new ByteArrayInputStream(
body.getBytes(servletResponse.getCharacterEncoding()));
}
else {
is = context.getResponseDataStream();
if (is!=null && context.getResponseGZipped()) {
if (isGzipRequested(context)) { servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
}
else {
is = handleGzipStream(is);
}
}
}
if (is!=null) {
writeResponse(is, outStream);
}
}
finally {
*
* @author Johannes Edmeier
*/
if (is != null) {
try {
is.close();
}
catch (Exception ex) {
log.warn("Error while closing upstream input stream", ex);
}
}
try {
Object zuulResponse = context.get("zuulResponse");
if (zuulResponse instanceof Closeable) {
((Closeable) zuulResponse).close();
}
outStream.flush();
// The container will close the stream for us
}
catch (IOException ex) {
log.warn("Error while sending response to client: " + ex.getMessage());
}
}
}
解决需求
在zuul中执行 身份验证
自定义一个前置 过滤器 设置类型为 pre,验证不通过则执行代码 RequestContext.getCurrentContext().ctx.setSendZuulResponse(false);
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
// 判断是否需要验证身份
String requestURI = ctx.getRequest().getRequestURI();
if (!needFilter(requestURI)){
return null;
}
// 获取原请求头中的 Authorization
String authorization = ctx.getRequest().getHeader("Authorization");
// 放入路由后的请求头中
ctx.getZuulRequestHeaders().put("Authorization",authorization);
// token 验证
SSOState result = ValidateService.jwtValidate(authorization);
if (!(result == SSOState.Success)) {
// ====================验证不通过===================
// 设置不再执行 RibbonRoutingFilter 过滤器
ctx.setSendZuulResponse(false);
// 设置返回状态
ctx.setResponseStatusCode(401);
// 设置自己想要返回的内容
ctx.setResponseBody(result.toString());
ctx.getResponse().setContentType("text/html;charset=UTF-8");
}
return null;
}
拦截请求的结果
定义一个后置的 过滤器:post
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
String responseBody = ctx.getResponseBody();
// 这里获取的是真正返回到浏览器的数据
System.out.println("=============="+responseBody);
try {
Object zuulResponse = RequestContext.getCurrentContext().get("zuulResponse");
if (zuulResponse != null) {
RibbonHttpResponse resp = (RibbonHttpResponse) zuulResponse;
String body = IOUtils.toString(resp.getBody());
// 这里获取的是 RibbonRoutingFilter 执行完后返回的数据,前提是让这个过滤器执行了的
System.err.println("+++++++++++++++++"+body);
resp.close();
RequestContext.getCurrentContext().setResponseBody(body);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
- 返回自己想输出的结果
在 SendResponseFilter 过滤器之前执行 RequestContext.getCurrentContext().setResponseBody(“you want to response!!!”);