详解SpringCloud Zuul过滤器返回值拦截
Zuul作为网关服务,是其他各服务对外中转站,通过Zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加密信息等等。
举个例子,用户服务提供一个登录接口,用户名密码正确后返回一个Token,此Token作为用户服务的通行证,那么用户登录成功后返回的Token就需要进行加密或者防止篡改处理。在到达用户服务其他接口前,就需要对Token进行校验,非法的Token就不需要转发到用户服务中了,直接在网关层返回信息即可。
要修改服务返回的信息,需要使用的是Zuul的过滤器。使用时只需要继承ZuulFilter,实现必要的方法即可。
Zuul提供默认的四种过滤器类型,通过filterType方法进行标识
- pre:可以在请求被路由之前调用
- route:在路由请求时候被调用
- post:在route和error过滤器之后被调用
- error:处理请求时发生错误时被调用
过滤器执行的顺序是通过filterOrder方法进行排序,越小的值越优先处理。FilterConstants定义了一些列默认的过滤器的执行顺序和路由类型,大部分需要用到的常量都在这儿。
例子中说明的,只有登录接口需要拦截,所以只需要拦截登录请求(/user/login)即可。可以通过过滤器的shouldFilter方法进行判断是否需要拦截。
由于是在准发用户服务成功后进行的数据修改,所以拦截器的类型时post类型的。整个类的实现如下:
publicclassAuthResponseFilterextendsAbstractZuulFilter{
privatestaticfinalStringRESPONSE_KEY_TOKEN="token";
@Value("${system.config.authFilter.authUrl}")
privateStringauthUrl;
@Value("${system.config.authFilter.tokenKey}")
privateStringtokenKey=RESPONSE_KEY_TOKEN;
@Autowired
privateAuthApiauthApi;
@Override
publicbooleanshouldFilter(){
RequestContextcontext=getCurrentContext();
returnStringUtils.equals(context.getRequest().getRequestURI().toString(),authUrl);
}
@Override
publicObjectrun(){
try{
RequestContextcontext=getCurrentContext();
InputStreamstream=context.getResponseDataStream();
Stringbody=StreamUtils.copyToString(stream,Charset.forName("UTF-8"));
if(StringUtils.isNotBlank(body)){
Gsongson=newGson();
@SuppressWarnings("unchecked")
Mapresult=gson.fromJson(body,Map.class);
if(StringUtils.isNotBlank(result.get(tokenKey))){
AuthModelauthResult=authApi.encodeToken(result.get(tokenKey));
if(authResult.getStatus()!=HttpServletResponse.SC_OK){
thrownewIllegalArgumentException(authResult.getErrMsg());
}
StringaccessToken=authResult.getToken();
result.put(tokenKey,accessToken);
}
body=gson.toJson(result);
}
context.setResponseBody(body);
}catch(IOExceptione){
rethrowRuntimeException(e);
}
returnnull;
}
@Override
publicStringfilterType(){
returnFilterConstants.POST_TYPE;
}
@Override
publicintfilterOrder(){
returnFilterConstants.SEND_RESPONSE_FILTER_ORDER-2;
}
}
配置文件,中添加授权url和返回token的key:
system.config.authFilter.authUrl=/user/login
system.config.authFilter.tokenKey=token
context.setResponseBody(body);这段代码是核心,通过此方法修改返回数据。
当用户登录成功后,根据返回的token,通过授权服务进行token加密,这里加密方式使用的是JWT。防止用户篡改信息,非法的请求直接可以拦截在网关层。
关于Zuul过滤器的执行过程,这里不需要多说明,源码一看便知,ZuulServletFilter:
@Override
publicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{
try{
init((HttpServletRequest)servletRequest,(HttpServletResponse)servletResponse);
try{
preRouting();
}catch(ZuulExceptione){
error(e);
postRouting();
return;
}
//Onlyforwardontotothechainifazuulresponseisnotbeingsent
if(!RequestContext.getCurrentContext().sendZuulResponse()){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
try{
routing();
}catch(ZuulExceptione){
error(e);
postRouting();
return;
}
try{
postRouting();
}catch(ZuulExceptione){
error(e);
return;
}
}catch(Throwablee){
error(newZuulException(e,500,"UNCAUGHT_EXCEPTION_FROM_FILTER_"+e.getClass().getName()));
}finally{
RequestContext.getCurrentContext().unset();
}
}
方法说明:
- preRoute:执行pre类型的过滤器
- postRoute:执行post类型的过滤器
- route:执行route类型的过滤器
- error:执行error类型的过滤器
通过context.setSendZuulResponse(false)可以终止请求的转发,但是只在pre类型的过滤器中设置才可以。
关于如何终止过滤器:
只有pre类型的过滤器支持终止转发,其他过滤器都是按照顺序执行的,而且pre类型的过滤器也只有在所有pre过滤器执行完后才可以终止转发,做不到终止过滤器继续执行。看ZuulServletFilter源码代码:
//Onlyforwardontotothechainifazuulresponseisnotbeingsent
if(!RequestContext.getCurrentContext().sendZuulResponse()){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
本文中的代码已提交至:https://gitee.com/cmlbeliever/springcloud欢迎Star
实现类在:api-getway工程下的com.cml.springcloud.api.filter.AuthResponseFilter
本地地址:http://xz.jb51.net:81/201806/yuanma/cmlbeliever-springcloud_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。