Merge remote-tracking branch 'origin/dev' into dev
commit
5f0b79bb05
|
|
@ -0,0 +1,195 @@
|
||||||
|
package org.qinan.safetyeval.adapter.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import com.jjb.saas.framework.core.filter.XssFilter;
|
||||||
|
import com.jjb.saas.framework.core.filter.XssHttpRequestWrapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ReadListener;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 框架 XssFilter / LoggingBodyFilter 会按文本处理 body,破坏 WPS 二进制上传。
|
||||||
|
* <p>
|
||||||
|
* /safetyEval-h5/**:用可重复读的 byte[] 包装请求(不改 Interceptor 类型,避免启动失败);
|
||||||
|
* 其它接口仍走原 XSS / LoggingBodyFilter。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class XssFilterOverrideConfig {
|
||||||
|
|
||||||
|
private static final String H5_URI_MARKER = "/safetyEval-h5/";
|
||||||
|
private static final String FRAMEWORK_XSS_BEAN_NAME = "xssFilterFilterRegistrationBean";
|
||||||
|
private static final String LOGGING_BODY_FILTER_BEAN_NAME = "loggingBodyFilter";
|
||||||
|
private static final String LOGGING_BODY_FILTER_CLASS =
|
||||||
|
"org.minbox.framework.logging.client.filter.LoggingBodyFilter";
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static BeanPostProcessor disableBinaryCorruptingFilters() {
|
||||||
|
return new BeanPostProcessor() {
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
if (bean instanceof FilterRegistrationBean) {
|
||||||
|
return processFilterRegistration((FilterRegistrationBean<?>) bean, beanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bean instanceof Filter
|
||||||
|
&& (LOGGING_BODY_FILTER_BEAN_NAME.equals(beanName)
|
||||||
|
|| LOGGING_BODY_FILTER_CLASS.equals(bean.getClass().getName()))) {
|
||||||
|
Filter original = (Filter) bean;
|
||||||
|
log.info("wrap LoggingBodyFilter beanName={}, class={}", beanName, bean.getClass().getName());
|
||||||
|
return new OncePerRequestFilter() {
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain chain) throws ServletException, IOException {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
if (isH5Uri(uri)) {
|
||||||
|
byte[] body = IoUtil.readBytes(request.getInputStream());
|
||||||
|
log.info("h5 binary body cached, uri={}, size={}", uri, body.length);
|
||||||
|
chain.doFilter(new RepeatableByteRequestWrapper(request, body), response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
original.doFilter(request, response, chain);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object processFilterRegistration(FilterRegistrationBean<?> registration, String beanName) {
|
||||||
|
if (beanName.startsWith("safety")
|
||||||
|
|| "safetyXssFilterRegistration".equals(beanName)) {
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
Filter filter = null;
|
||||||
|
try {
|
||||||
|
filter = registration.getFilter();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// ignore uninitialized registration
|
||||||
|
}
|
||||||
|
boolean match = FRAMEWORK_XSS_BEAN_NAME.equals(beanName) || filter instanceof XssFilter;
|
||||||
|
if (match) {
|
||||||
|
registration.setEnabled(false);
|
||||||
|
log.info("disabled framework XSS filter registration: beanName={}", beanName);
|
||||||
|
}
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<Filter> safetyXssFilterRegistration() {
|
||||||
|
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
|
||||||
|
bean.setFilter(new OncePerRequestFilter() {
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain chain) throws ServletException, IOException {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
if (isH5Uri(uri)) {
|
||||||
|
log.info("xss bypass for h5 uri={}", uri);
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chain.doFilter(new XssHttpRequestWrapper(request), response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bean.setName("safetyXssFilter");
|
||||||
|
bean.addUrlPatterns("/*");
|
||||||
|
bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 20);
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isH5Uri(String uri) {
|
||||||
|
return uri != null && uri.contains(H5_URI_MARKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次 getInputStream/getReader 都从同一份 byte[] 新建流,
|
||||||
|
* 避免 LoggingWebInterceptor 里 new RequestWrapper 关闭流后 Controller 读不到。
|
||||||
|
*/
|
||||||
|
static final class RepeatableByteRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private final byte[] body;
|
||||||
|
|
||||||
|
RepeatableByteRequestWrapper(HttpServletRequest request, byte[] body) {
|
||||||
|
super(request);
|
||||||
|
this.body = body == null ? new byte[0] : body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() {
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
|
||||||
|
return new ServletInputStream() {
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return inputStream.available() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
// not used
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() {
|
||||||
|
return inputStream.read();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedReader getReader() {
|
||||||
|
Charset charset = resolveCharset();
|
||||||
|
return new BufferedReader(new InputStreamReader(getInputStream(), charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getContentLength() {
|
||||||
|
return body.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLengthLong() {
|
||||||
|
return body.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Charset resolveCharset() {
|
||||||
|
String encoding = getCharacterEncoding();
|
||||||
|
if (encoding == null || encoding.isEmpty()) {
|
||||||
|
return StandardCharsets.UTF_8;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Charset.forName(encoding);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return StandardCharsets.UTF_8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -172,7 +172,7 @@ public class WpsExecutor implements WpsApi {
|
||||||
public Map<String, Object> uploadBinary(String fileId, InputStream inputStream) {
|
public Map<String, Object> uploadBinary(String fileId, InputStream inputStream) {
|
||||||
try {
|
try {
|
||||||
wpsDocDomainService.getByFileId(fileId);
|
wpsDocDomainService.getByFileId(fileId);
|
||||||
FileInfo ossFileInfo = fileStorageService.of(inputStream, fileId).upload();
|
FileInfo ossFileInfo = fileStorageService.of(inputStream, fileId).setSaveFilename(UUID.randomUUID().toString().replace("-", "")+".docx").upload();
|
||||||
FileStorage fileStorage = FileStorageHelper.buildResource(ossFileInfo, fileId, true);
|
FileStorage fileStorage = FileStorageHelper.buildResource(ossFileInfo, fileId, true);
|
||||||
FileStorage saved = fileStorageDomainService.saveResource(fileStorage);
|
FileStorage saved = fileStorageDomainService.saveResource(fileStorage);
|
||||||
wpsDocDomainService.savePendingUpload(fileId, saved.getId(), saved.getUrl(), saved.getSize());
|
wpsDocDomainService.savePendingUpload(fileId, saved.getId(), saved.getUrl(), saved.getSize());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue