需求:在filter中进行请求日志的打印(有时候是因为数据的序列化出错),做简单鉴权
java中springboot,request body InputStream流的读取只能读取一次,读完就没,如果在filter中直接读取了,到controller中数据就拿不到了。所以springboot提供了 ContentCachingRequestWrapper 这个类来进行包裹。
先看这个类的名称可以踩个大概就是做request的缓存用的。
// 代码如下,基本原理是将原始的request初始化wrappedRequest,然后用这个变量贯穿整个请求过程,
// 值得注意的是wrappedRequest这个工作原理是read()函数被调用的过程中缓存,也就是说这个原理是springboot/mvc在controller进行body数据读取的时候,读出来了再把数据copy一份到cache中,所以wrappedRequest就能反复读取数据。
// 在下列代码中 执行的顺序 是 doFilter() 调用 read()函数,wrappedRequest中的数据就被copy完毕,所以recordReq() 和 recordResp() 函数参数就能访问到请求数据,如果不使用wrappedRequest,那么数据读取完后request中数据就空了。
// 这边值得注意的是在权限验证失败时候手动调用了wrappedRequest.getReader().read(),意在将数据刷到wrappedRequest中。
@Component
@WebFilter(filterName = "authenticationFilter", urlPatterns = "/*")
@Order(-9999)
@Slf4j
public class AuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);
// 授权认证
Optional<String> origin = authentication(wrappedRequest);
long start = TimeUtils.getLongTimeMillis();
if (origin.isEmpty()) {
// 手动读取,将结果刷新到wrappedRequest中
wrappedRequest.getReader().read();
// 为携带Token
wrappedResponse.setStatus(HttpServletResponse.SC_OK);
String str = GsonUtils.toJson(BaseResponse.fail(ResultCode.AUTHORIZE_FAILED));
//设置 HttpServletResponse使用utf-8编码
wrappedResponse.setCharacterEncoding("utf-8");
//设置响应头的编码
wrappedResponse.setHeader("Content-Type", "application/json;charset=utf-8");
wrappedResponse.getWriter().write(str);
} else {
chain.doFilter(wrappedRequest, wrappedResponse);
}
long end = TimeUtils.getLongTimeMillis();
// 记录请求信息
recordReq(wrappedRequest, origin);
// 记录response信息
recordResp(wrappedRequest, wrappedResponse, end - start);
// 这一步很重要,把缓存的响应内容,输出到客户端
wrappedResponse.copyBodyToResponse();
}
}