1.问题
- 项目已经上线运行了一段时间了,突然某些接口的出参需要加密(对称加密);
- 项目是分布式的,有很多服务(微服务项目);
2.解决
根据上述问题,我设计了解决方案(该方案只能解决得到的报文是加密的!!!):
- 由于很多服务,所以拦截的地方放在网关最合适;
- 由于是成品项目,已经运行了很长时间,所以只能对部分响应接口做加密处理,根据header里放约定值(怎么知道那些接口需要加密呢,🤭咱们让前端告诉【也可以后台写死对应的接口】),虽然前端告诉,设计不怎么安全;
2.1 总所周知,在网关继承抽象ZuulFilter
package com.zjrcinfo.zjguahao.gateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
/**
* 针对响应信息需要进行加密处理
*
* @author YanZhen
* @date 2021/03/22 11:12
*/
public class ZuulResultEncryptFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(ZuulResultEncryptFilter.class);
/**
* 该过滤器执行的顺序,路由后拦截
*
* @return post 路由后拦截
*/
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
/**
* 执行顺序,在第1000位执行
*
* @return 第1000位
*/
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER;
}
/**
* @return true该过滤器生效
*/
@Override
public boolean shouldFilter() {
// todo:此次可以用户后台配置的url进行判断
// 获取上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
// 与前端预定好的自定义key,放在header里
String encrypt = request.getHeader("custom_head_key");
// 当存在加密行为时,该过滤器生效
// TODO:可以自定义结果进行比较
return StringUtils.isNotBlank(encrypt);
}
@Override
public Object run() throws ZuulException {
String respBody;
// 响应
RequestContext currentContext = RequestContext.getCurrentContext();
// TODO:RequestContext里有很多获取返回值的方法,此方法取不到值时,挨个试试
InputStream responseDataStream = currentContext.getResponseDataStream();
if (responseDataStream == null) {
return null;
}
// 响应体获取
try {
respBody = IOUtils.toString(responseDataStream);
} catch (IOException e) {
logger.error("响应加密时,获取响应信息失败!", e);
throw new ZuulException("响应加密时,获取响应信息失败!", 500, e.getMessage());
}
// 加密
try {
// TODO:选择DES加密,然后赋值到body里,上面不管怎么取到的赋值总是body;
currentContext.setResponseBody(DESUtil.encryptDES(respBody));
} catch (Exception e) {
logger.error("响应加密时,异常:{}", respBody, e);
throw new ZuulException("响应加密时,异常!", 500, e.getMessage());
}
return null;
}
}
2.2 注入到容器
“兄弟,一定写好注释,尊重一下同事,让他们看看你当时的想法!!!”
/**
* 为指定响应信息加密的接口加密
* @return 响应信息加密
*/
@Bean
public ZuulResultEncryptFilter zuulResultEncryptFilter() {
return new ZuulResultEncryptFilter();
}
3.结论
继承ZuulFilter时默认有四个方法:
- filterType:调用路由执行顺序(不知道的翻翻ZuulFilter类上或FilterConstants池子看看error、post、pre、route注释);
- filterOrder:当filterType相等时,执行的顺序(小的先执行);
- shouldFilter:在一个action里是否生效(不要上去就是一个true,执行过滤器需要时间的);
- run:在一个action里,拦截后你想做什么;