1.介绍
解决HttpServletRequest只能读取一次流后无法获取的问题,采用HttpServletRequestWrapper进行包装,通过继承HttpServletRequestWrapper类实现读取流的操作。
解决整合Filter过滤器时遇到的问题:过滤器urlPatterns不生效、过滤器不起作用。
2.应用场景
基于客户对安全性的考虑,部分请求中存在敏感数据需要进行加密传输,故采用过滤器对特定URL进行解密。
将整串json格式请求加密过的字符串进行解密,解析出正确的json字符串后进行业务处理。
目前接口响应不存在敏感信息未进行处理,若需要处理响应数据可以使用ResponseWrapper进行加密处理。
3.实操
3.1 创建RequestWrapper继承HttpServletRequestWrapper
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] bytes;
private HttpServletRequest request;
/**
* @param request
*/
public RequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
public String getRequestBody() throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
bytes = baos.toByteArray();
String body = new String(bytes);
return body;
} catch (IOException ex) {
throw ex;
}
}
public void setRequestBody(String body){
bytes = body.getBytes();
}
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
}
3.2 创建ResponseWrapper继承HttpServletResponseWrapper
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream outputStream;
public byte[] getResponseData(){
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
public ResponseWrapper(HttpServletResponse response) {
super(response);
this.outputStream =new ByteArrayOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(outputStream);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
};
}
}
3.3新增过滤器实现Filter类
- @WebFilter不得与@Component一起使用,会导致双重注册,urlPatterns失效会对全部请求过滤
- @WebFilter的urlPatterns不支持 /** ,会导致过滤器失效,请求无法进入过滤器
- 使用@WebFilter应在启动类加@ServletComponentScan注解
- 使用@ServletComponentScan需要过滤器包名,否则无法加载到过滤器
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(filterName="TtFilter", urlPatterns="/api/tt/*")
@Order(0)
@Slf4j
public class TtFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//log.info("进入过滤器");
// 请求解密
if (("POST".equals(((HttpServletRequest)request).getMethod().trim().toUpperCase())) && "application/json".equals(request.getContentType().trim().toLowerCase())){
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
String bodyStr = requestWrapper.getRequestBody();
if(StringUtils.isNotBlank(bodyStr)){
log.info("解密前 API 请求消息:type={}, requestBody={}", requestWrapper.getMethod(), bodyStr);
//解密
String plainText = PBEUtils.decrypt(bodyStr);
int lastIndex = plainText.lastIndexOf(":");
int secondLastIndex = plainText.lastIndexOf(":", lastIndex - 1);
plainText = plainText.substring(0, secondLastIndex);
log.info("解密后requestBody={}",plainText);
requestWrapper.setRequestBody(plainText);
}
chain.doFilter(requestWrapper, response);
}else{
// 处理业务逻辑
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
加密工具
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
public class PBEUtils {
public static String decrypt(String cipher) {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setAlgorithm("PBEWithMD5AndDES");//基于 MD5 和 DES 的密码算法
encryptor.setPassword("xxxx");//密码
return encryptor.decrypt(cipher);
}
public static String encrypt(String cipher) {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setAlgorithm("PBEWithMD5AndDES");
encryptor.setPassword("xxxx");
return encryptor.encrypt(cipher);
}
}
3.4 启动类添加注解@ServletComponentScan("自己的包名")
@SpringBootApplication(scanBasePackages = {"com.xxx.xxx.bsp", "com.xxx.hfmis", "com.xxx.wxdb"})
@EnableFusionAuth
@ServletComponentScan("com.xxx.xxx.filter")
public class HfmisUiSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(HfmisUiSpringBootApplication.class, args);
}
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
return factory -> {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/ui/index.html");
factory.addErrorPages(error404Page);
};
}
}