詳解SpringCloud Zuul過(guò)濾器返回值攔截
Zuul作為網(wǎng)關(guān)服務(wù),是其他各服務(wù)對(duì)外中轉(zhuǎn)站,通過(guò)Zuul進(jìn)行請(qǐng)求轉(zhuǎn)發(fā)。這就涉及到部分?jǐn)?shù)據(jù)是不能原封返回的,比如服務(wù)之間通信的憑證,用戶的加密信息等等。
舉個(gè)例子,用戶服務(wù)提供一個(gè)登錄接口,用戶名密碼正確后返回一個(gè)Token,此Token作為用戶服務(wù)的通行證,那么用戶登錄成功后返回的Token就需要進(jìn)行加密或者防止篡改處理。在到達(dá)用戶服務(wù)其他接口前,就需要對(duì)Token進(jìn)行校驗(yàn),非法的Token就不需要轉(zhuǎn)發(fā)到用戶服務(wù)中了,直接在網(wǎng)關(guān)層返回信息即可。
要修改服務(wù)返回的信息,需要使用的是Zuul的過(guò)濾器。使用時(shí)只需要繼承ZuulFilter,實(shí)現(xiàn)必要的方法即可。
Zuul提供默認(rèn)的四種過(guò)濾器類型,通過(guò)filterType方法進(jìn)行標(biāo)識(shí)
- pre:可以在請(qǐng)求被路由之前調(diào)用
- route:在路由請(qǐng)求時(shí)候被調(diào)用
- post:在route和error過(guò)濾器之后被調(diào)用
- error:處理請(qǐng)求時(shí)發(fā)生錯(cuò)誤時(shí)被調(diào)用
過(guò)濾器執(zhí)行的順序是通過(guò)filterOrder方法進(jìn)行排序,越小的值越優(yōu)先處理。FilterConstants定義了一些列默認(rèn)的過(guò)濾器的執(zhí)行順序和路由類型,大部分需要用到的常量都在這兒。
例子中說(shuō)明的,只有登錄接口需要攔截,所以只需要攔截登錄請(qǐng)求(/user/login)即可??梢酝ㄟ^(guò)過(guò)濾器的shouldFilter方法進(jìn)行判斷是否需要攔截。
由于是在準(zhǔn)發(fā)用戶服務(wù)成功后進(jìn)行的數(shù)據(jù)修改,所以攔截器的類型時(shí)post類型的。整個(gè)類的實(shí)現(xiàn)如下:
public class AuthResponseFilter extends AbstractZuulFilter {
private static final String RESPONSE_KEY_TOKEN = "token";
@Value("${system.config.authFilter.authUrl}")
private String authUrl;
@Value("${system.config.authFilter.tokenKey}")
private String tokenKey = RESPONSE_KEY_TOKEN;
@Autowired
private AuthApi authApi;
@Override
public boolean shouldFilter() {
RequestContext context = getCurrentContext();
return StringUtils.equals(context.getRequest().getRequestURI().toString(), authUrl);
}
@Override
public Object run() {
try {
RequestContext context = getCurrentContext();
InputStream stream = context.getResponseDataStream();
String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
if (StringUtils.isNotBlank(body)) {
Gson gson = new Gson();
@SuppressWarnings("unchecked")
Map<String, String> result = gson.fromJson(body, Map.class);
if (StringUtils.isNotBlank(result.get(tokenKey))) {
AuthModel authResult = authApi.encodeToken(result.get(tokenKey));
if (authResult.getStatus() != HttpServletResponse.SC_OK) {
throw new IllegalArgumentException(authResult.getErrMsg());
}
String accessToken = authResult.getToken();
result.put(tokenKey, accessToken);
}
body = gson.toJson(result);
}
context.setResponseBody(body);
} catch (IOException e) {
rethrowRuntimeException(e);
}
return null;
}
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 2;
}
}
配置文件,中添加授權(quán)url和返回token的key:
system.config.authFilter.authUrl=/user/login
system.config.authFilter.tokenKey=token
context.setResponseBody(body);這段代碼是核心,通過(guò)此方法修改返回?cái)?shù)據(jù)。
當(dāng)用戶登錄成功后,根據(jù)返回的token,通過(guò)授權(quán)服務(wù)進(jìn)行token加密,這里加密方式使用的是JWT。防止用戶篡改信息,非法的請(qǐng)求直接可以攔截在網(wǎng)關(guān)層。
關(guān)于Zuul過(guò)濾器的執(zhí)行過(guò)程,這里不需要多說(shuō)明,源碼一看便知,ZuulServletFilter:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
try {
preRouting();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
try {
postRouting();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
方法說(shuō)明:
- preRoute:執(zhí)行pre類型的過(guò)濾器
- postRoute:執(zhí)行post類型的過(guò)濾器
- route:執(zhí)行route類型的過(guò)濾器
- error:執(zhí)行error類型的過(guò)濾器
通過(guò)context.setSendZuulResponse(false)可以終止請(qǐng)求的轉(zhuǎn)發(fā),但是只在pre類型的過(guò)濾器中設(shè)置才可以。
關(guān)于如何終止過(guò)濾器:
只有pre類型的過(guò)濾器支持終止轉(zhuǎn)發(fā),其他過(guò)濾器都是按照順序執(zhí)行的,而且pre類型的過(guò)濾器也只有在所有pre過(guò)濾器執(zhí)行完后才可以終止轉(zhuǎn)發(fā),做不到終止過(guò)濾器繼續(xù)執(zhí)行。看ZuulServletFilter源碼代碼:
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
本文中的代碼已提交至: https://gitee.com/cmlbeliever/springcloud 歡迎Star
實(shí)現(xiàn)類在:api-getway工程下的com.cml.springcloud.api.filter.AuthResponseFilter
本地地址:http://xz.jb51.net:81/201806/yuanma/cmlbeliever-springcloud_jb51.rar
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于注解式的分布式Elasticsearch的封裝案例
這篇文章主要介紹了關(guān)于注解式的分布式Elasticsearch的封裝案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
SpringBoot 錯(cuò)誤處理機(jī)制與自定義錯(cuò)誤處理實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringBoot 錯(cuò)誤處理機(jī)制與自定義錯(cuò)誤處理實(shí)現(xiàn)詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
使用Java語(yǔ)言將XML轉(zhuǎn)為PDF的方法
這篇文章主要介紹了使用Java語(yǔ)言將XML轉(zhuǎn)為PDF的方法,本文將介紹通過(guò)Java代碼來(lái)實(shí)現(xiàn)該格式轉(zhuǎn)換的方法,需要的朋友可以參考下2022-03-03
SpringBoot中MyBatis-Plus 查詢時(shí)排除某些字段的操作方法
這篇文章主要介紹了SpringBoot中MyBatis-Plus 查詢時(shí)排除某些字段的操作方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
Netty分布式flush方法刷新buffer隊(duì)列源碼剖析
這篇文章主要為大家介紹了Netty分布式flush方法刷新buffer隊(duì)列源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
Maven項(xiàng)目改為spring boot項(xiàng)目的方法圖解
這篇文章主要介紹了Maven項(xiàng)目改為spring boot項(xiàng)目的方法圖解 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
mybatis?報(bào)錯(cuò)顯示sql中有兩個(gè)limit的解決
這篇文章主要介紹了mybatis?報(bào)錯(cuò)顯示sql中有兩個(gè)limit的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Java實(shí)現(xiàn)經(jīng)典大富翁游戲的示例詳解
大富翁,又名地產(chǎn)大亨。是一種多人策略圖版游戲。參與者分得游戲金錢,憑運(yùn)氣(擲骰子)及交易策略,買地、建樓以賺取租金。本文將通過(guò)Java實(shí)現(xiàn)這一經(jīng)典游戲,感興趣的可以跟隨小編一起學(xué)習(xí)一下2022-02-02

