commit d20bd24a52eac45826e4c7f80dd5243f24f3d7e5 Author: pioneer Date: Tue Nov 29 15:55:16 2022 +0800 open diff --git a/README.md b/README.md new file mode 100644 index 0000000..43324cb --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# open-JSD-10202 + +JSD-10202 客户的文件都是加密的,所以数据填报时上传Excel动作需要先跑一下他们的解密应用,再读取到填报页面;同样在使用报表的导出功能时,需要先跑一下他们的加密应用,保障导出的文件是经过加密的\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系【pioneer】处理。 \ No newline at end of file diff --git a/lib/drm-edi-3.1.1.jar b/lib/drm-edi-3.1.1.jar new file mode 100644 index 0000000..8af4c19 Binary files /dev/null and b/lib/drm-edi-3.1.1.jar differ diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..6f91cdd --- /dev/null +++ b/plugin.xml @@ -0,0 +1,22 @@ + + + com.fr.plugin.xx.zyjn.decode + + yes + 1.0 + 10.0 + 2018-07-31 + fr.open + + + ]]> + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/DecodeConfig.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/DecodeConfig.java new file mode 100644 index 0000000..179b1dd --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/DecodeConfig.java @@ -0,0 +1,42 @@ +package com.fr.plugin.xx.zyjn.decode; + +import com.fr.config.*; +import com.fr.config.holder.Conf; +import com.fr.config.holder.factory.Holders; + + +/** + * @author xx + * @since 2021/12/04 + */ +@Visualization(category = "加解密配置") +public class DecodeConfig extends DefaultConfiguration { + + private static volatile DecodeConfig config = null; + + public static DecodeConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(DecodeConfig.class); + } + return config; + } + + @Identifier(value = "debugSwitch", name = "插件调试开关", description = "日志调试模式", status = Status.SHOW) + private Conf debugSwitch = Holders.simple(true); + + public Boolean getDebugSwitch() { + return this.debugSwitch.get(); + } + + public void setDebugSwitch(Boolean debugSwitch) { + this.debugSwitch.set(debugSwitch); + } + + + @Override + public Object clone() throws CloneNotSupportedException { + DecodeConfig cloned = (DecodeConfig) super.clone(); + cloned.debugSwitch = (Conf) debugSwitch.clone(); + return cloned; + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/GlobalFilter.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/GlobalFilter.java new file mode 100644 index 0000000..ce34b87 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/GlobalFilter.java @@ -0,0 +1,97 @@ +package com.fr.plugin.xx.zyjn.decode; + +import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.context.PluginContexts; +import com.fr.plugin.xx.zyjn.decode.decode.DecodeFilter; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.StringUtils; +import com.fr.stable.fun.Authorize; +import com.fr.web.utils.WebUtils; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author xx + * @date 2020/11/20 + */ +@FunctionRecorder +@Authorize(callSignKey = PluginConstants.PLUGIN_ID) +@EnableMetrics +public class GlobalFilter extends AbstractGlobalRequestFilterProvider { + @Override + public String filterName() { + return "zdgl"; + } + + @Override + @Focus(id = PluginConstants.PLUGIN_ID, text = PluginConstants.PLUGIN_NAME, source = Original.PLUGIN) + public String[] urlPatterns() { + if (PluginContexts.currentContext().isAvailable()) { + return new String[]{ + "/*" + }; + } else { + return new String[]{"/neverbeused"}; + } + } + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + + if (isDecode(req)) { + new DecodeFilter().doFilter(req, res, filterChain); + return; + } + /*if (isEncode(req)) { + new EncodeFilter().doFilter(req, res, filterChain); + return; + }*/ + + try { + filterChain.doFilter(req, res); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } catch (ServletException e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + } + + private boolean isDecode(HttpServletRequest req) { + if (req.getRequestURI().endsWith("attach/upload")) { + return true; + } + if (req.getRequestURI().endsWith("/dataset/upload")) { + return true; + } + String op = WebUtils.getHTTPRequestParameter(req, "op"); + String cmd = WebUtils.getHTTPRequestParameter(req, "cmd"); + if (StringUtils.equals("fr_attach", op) && StringUtils.equals("ah_upload", cmd)) { + return true; + } + if (StringUtils.equals("fr_write", op) && StringUtils.equals("imp_w_excel_data", cmd)) { + return true; + } + return false; + } + + private boolean isEncode(HttpServletRequest req) { + String op = WebUtils.getHTTPRequestParameter(req, "op"); + String cmd = WebUtils.getHTTPRequestParameter(req, "cmd"); + if (StringUtils.equals("fr_attach", op) && StringUtils.equals("ah_download", cmd)) { + return true; + } + if (StringUtils.equals("customcommit", op) && StringUtils.equals("downloadfile", cmd)) { + return true; + } + return false; + } + +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/LRGT.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/LRGT.java new file mode 100644 index 0000000..14a6ebb --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/LRGT.java @@ -0,0 +1,27 @@ +package com.fr.plugin.xx.zyjn.decode; + +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; + +/** + * 配置信息初始化 + */ + +public class LRGT extends AbstractPluginLifecycleMonitor { + @Override + public void afterRun(PluginContext pluginContext) { + DecodeConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + } + + @Override + public void beforeUninstall(PluginContext pluginContext) { + } + + @Override + public void afterInstall(PluginContext var1) { + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/LogUtils.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/LogUtils.java new file mode 100644 index 0000000..9863301 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/LogUtils.java @@ -0,0 +1,120 @@ +package com.fr.plugin.xx.zyjn.decode; + +import com.fr.log.FineLoggerFactory; +import com.fr.log.FineLoggerProvider; +import com.fr.plugin.context.PluginContexts; +import com.fr.stable.StringUtils; + +/** + * @author xx + * @since 2021/12/04 + */ +public final class LogUtils { + private static final String DEBUG_PREFIX = "[插件调试] "; + private static String LOG_PREFIX = PluginConstants.PLUGIN_NAME; + private static final String PLUGIN_VERSION; + + private static final FineLoggerProvider LOGGER = FineLoggerFactory.getLogger(); + + static { + String version = PluginContexts.currentContext().getMarker().getVersion(); + if (StringUtils.isNotBlank(version)) { + PLUGIN_VERSION = "[v" + version + "] "; + } else { + PLUGIN_VERSION = "[unknown version] "; + } + + LOG_PREFIX = LOG_PREFIX + PLUGIN_VERSION; + } + + public static void setPrefix(String prefix) { + if (prefix != null) { + LOG_PREFIX = prefix; + } + } + + public static boolean isDebugEnabled() { + return LOGGER.isDebugEnabled(); + } + + public static void debug(String s) { + LOGGER.debug(LOG_PREFIX + s); + } + + public static void debug(String s, Object... objects) { + LOGGER.debug(LOG_PREFIX + s, objects); + } + + public static void debug(String s, Throwable throwable) { + LOGGER.debug(LOG_PREFIX + s, throwable); + } + + public static void debug4plugin(String s) { + if (DecodeConfig.getInstance().getDebugSwitch()) { + LOGGER.error(DEBUG_PREFIX + LOG_PREFIX + s); + } else { + LOGGER.debug(LOG_PREFIX + s); + } + } + + public static void debug4plugin(String s, Object... objects) { + if (DecodeConfig.getInstance().getDebugSwitch()) { + LOGGER.error(DEBUG_PREFIX + LOG_PREFIX + s, objects); + } else { + LOGGER.debug(LOG_PREFIX + s, objects); + } + } + + public static void debug4plugin(String s, Throwable throwable) { + if (DecodeConfig.getInstance().getDebugSwitch()) { + LOGGER.error(DEBUG_PREFIX + LOG_PREFIX + s, throwable); + } else { + LOGGER.debug(LOG_PREFIX + s, throwable); + } + } + + + public static boolean isInfoEnabled() { + return LOGGER.isInfoEnabled(); + } + + public static void info(String s) { + LOGGER.info(LOG_PREFIX + s); + } + + public static void info(String s, Object... objects) { + LOGGER.info(LOG_PREFIX + s, objects); + } + + public static void warn(String s) { + LOGGER.warn(LOG_PREFIX + s); + } + + public static void warn(String s, Object... objects) { + LOGGER.warn(LOG_PREFIX + s, objects); + } + + public static void warn(String s, Throwable throwable) { + LOGGER.warn(LOG_PREFIX + s, throwable); + } + + public static void warn(Throwable throwable, String s, Object... objects) { + LOGGER.warn(throwable, LOG_PREFIX + s, objects); + } + + public static void error(String s) { + LOGGER.error(LOG_PREFIX + s); + } + + public static void error(String s, Object... objects) { + LOGGER.error(LOG_PREFIX + s, objects); + } + + public static void error(String s, Throwable throwable) { + LOGGER.error(LOG_PREFIX + s, throwable); + } + + public static void error(Throwable throwable, String s, Object... objects) { + LOGGER.error(throwable, LOG_PREFIX + s, objects); + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/PluginConstants.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/PluginConstants.java new file mode 100644 index 0000000..edc631d --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/PluginConstants.java @@ -0,0 +1,13 @@ +package com.fr.plugin.xx.zyjn.decode; + +/** + * @author xx + * @since 2021/12/04 + */ +public class PluginConstants { + + public static final String PLUGIN_ID = "com.fr.plugin.xx.zyjn.decode"; + + public static final String PLUGIN_NAME = "加密解密"; + +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/BodyReaderHttpServletRequestWrapper.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/BodyReaderHttpServletRequestWrapper.java new file mode 100644 index 0000000..5142a3a --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/BodyReaderHttpServletRequestWrapper.java @@ -0,0 +1,64 @@ +package com.fr.plugin.xx.zyjn.decode.decode; + +import com.fr.third.jodd.io.StreamUtil; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * @author xx + * @date 2022/03/01 + */ +public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { + private BufferedReader br; + private byte[] body; + + public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + //this.br = request.getReader(); + body = StreamUtil.readBytes(request.getInputStream()); + } + + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + 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 bais.read(); + } + }; + } + + public void setBody(byte[] body) { + this.body = body; + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/DecodeFilter.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/DecodeFilter.java new file mode 100644 index 0000000..ec52a93 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/decode/DecodeFilter.java @@ -0,0 +1,206 @@ +package com.fr.plugin.xx.zyjn.decode.decode; + +import cn.net.drm.edi.client.DrmAgent; +import cn.net.drm.edi.client.DrmAgentInf; +import cn.net.drm.edi.exception.DrmException; +import com.fr.base.ServerConfig; +import com.fr.base.core.IgnoreBytesInputStream; +import com.fr.base.core.ParseResult; +import com.fr.base.core.PostParseUtils; +import com.fr.data.NetworkHelper; +import com.fr.decision.authority.data.User; +import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; +import com.fr.decision.mobile.terminal.TerminalHandler; +import com.fr.decision.webservice.v10.config.ConfigService; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.login.TokenResource; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.general.CommonIOUtils; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.plugin.context.PluginContexts; +import com.fr.plugin.xx.zyjn.decode.LogUtils; +import com.fr.plugin.xx.zyjn.decode.PluginConstants; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.stable.fun.Authorize; +import com.fr.stable.web.Device; +import com.fr.third.org.apache.commons.io.IOUtils; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.util.UUID; + +/** + * @author xx + * @date 2022/03/01 + */ +@FunctionRecorder +@Authorize(callSignKey = PluginConstants.PLUGIN_ID) +public class DecodeFilter extends AbstractGlobalRequestFilterProvider { + + private static final byte[] NEW_LINE_BYTES = new byte[]{13, 10}; + private static final byte[] BOUNDARY_END = new byte[]{45, 45}; + + private static final String TMP_PATH = System.getProperty("java.io.tmpdir"); + + @Override + public String filterName() { + return "decode"; + } + + @Override + public String[] urlPatterns() { + String servletPathName = "decision"; + try { + servletPathName = ConfigService.getInstance().getBasicParam().getServletPathName(); + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } + if (PluginContexts.currentContext().isAvailable()) { + return new String[]{ + "/" + servletPathName + "/v10/attach/upload", + "/" + servletPathName + "/v10/dataset/upload", + "/" + servletPathName + "/view/report", + + }; + } else { + return new String[]{"/aaaa"}; + } + + } + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + BodyReaderHttpServletRequestWrapper wrapper = null; + try { + DrmAgentInf instance; + instance = DrmAgent.getInstance(); + wrapper = new BodyReaderHttpServletRequestWrapper(req); + User user = getUser(req); + if (user == null) { + LogUtils.debug4plugin("current user is null,not decrypted"); + filter(wrapper, res, filterChain); + return; + } + InputStream inputStream = wrapper.getInputStream(); + ParseResult parseResult = PostParseUtils.parse(inputStream, req.getCharacterEncoding()); + InputStream fis = new IgnoreBytesInputStream(inputStream, this.concat(this.concat(NEW_LINE_BYTES, parseResult.getBoundary().getBytes()), BOUNDARY_END)); + LogUtils.debug4plugin("record file request url is {}", req.getRequestURI() + req.getQueryString()); + File file = saveTmp(fis, parseResult.getFileName()); + boolean encrypted = instance.isEncrypted(file); + LogUtils.debug4plugin("current files encrypted is {}", encrypted); + if (!encrypted) { + filter(wrapper, res, filterChain); + return; + } + decode(req, wrapper, parseResult, file, instance); + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } + if (wrapper != null) { + filter(wrapper, res, filterChain); + return; + } + } + + private void decode(HttpServletRequest req, BodyReaderHttpServletRequestWrapper wrapper, ParseResult parseResult, File file, DrmAgentInf instance) throws DrmException, UnsupportedEncodingException, FileNotFoundException { + //File file = saveTmp(fis,parseResult.getFileName()); + long decrypt = instance.decrypt(file); + LogUtils.debug4plugin("decode file size is {}", decrypt); + wrapper.setBody(handleBody(req, CommonIOUtils.inputStream2Bytes(new FileInputStream(file)), parseResult, req.getCharacterEncoding() == null ? ServerConfig.getInstance().getServerCharset() : req.getCharacterEncoding())); + file.delete(); + LogUtils.debug4plugin("delete temp file"); + } + + private User getUser(HttpServletRequest req) { + try { + return UserService.getInstance().getUserByRequestCookie(req); + } catch (Exception e) { + + } + return null; + } + + private File saveTmp(InputStream input, String filename) { + if (!ResourceIOUtils.isDirectoryExist(TMP_PATH)) { + ResourceIOUtils.createDirectory(TMP_PATH); + } + String path = TMP_PATH + File.separator + UUID.randomUUID() + filename.substring(filename.indexOf(".")); + File file = new File(path); + FileOutputStream outputStream = null; + if (!file.exists()) { + try { + file.createNewFile(); + outputStream = new FileOutputStream(file.getPath()); + IOUtils.copyLarge(input, outputStream); + LogUtils.debug4plugin("save tmp file path is {}", path); + } catch (IOException e) { + LogUtils.error(e.getMessage(), e); + } finally { + try { + input.close(); + outputStream.flush(); + outputStream.close(); + } catch (IOException e) { + LogUtils.error(e.getMessage(), e); + } + } + } + return new File(path); + } + + + private void filter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + filterChain.doFilter(req, res); + } catch (IOException e) { + LogUtils.error(e.getMessage(), e); + } catch (ServletException e) { + LogUtils.error(e.getMessage(), e); + } + } + + private boolean isLogin(HttpServletRequest request) { + String oldToken = TokenResource.COOKIE.getToken(request); + return oldToken != null && checkTokenValid(request, (String) oldToken); + } + + private boolean checkTokenValid(HttpServletRequest req, String token) { + try { + Device device = NetworkHelper.getDevice(req); + LoginService.getInstance().loginStatusValid(token, TerminalHandler.getTerminal(req, device)); + return true; + } catch (Exception ignore) { + } + return false; + } + + private byte[] concat(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + private byte[] handleBody(HttpServletRequest req, byte[] body, ParseResult parseResult, String charsetName) throws UnsupportedEncodingException { + byte[] newBody; + byte[] start; + byte[] end; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(parseResult.getBoundary()).append("\r\n") + .append("Content-Disposition:").append(parseResult.getDispline()) + .append("; name=\"").append(parseResult.getFieldName()) + .append("\"; filename=\"").append(parseResult.getFileName()).append("\"\r\n") + .append("Content-Type: ").append(req.getContentType()).append("\r\n").append("\r\n"); + start = stringBuilder.toString().getBytes(charsetName); + StringBuilder stringBuilder1 = new StringBuilder(); + stringBuilder1.append("\r\n").append(parseResult.getBoundary()).append("--\r\n"); + end = stringBuilder1.toString().getBytes(charsetName); + + newBody = this.concat(this.concat(start, body), end); + return newBody; + } + +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncodeExportExtensionProvider.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncodeExportExtensionProvider.java new file mode 100644 index 0000000..046db5d --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncodeExportExtensionProvider.java @@ -0,0 +1,28 @@ +package com.fr.plugin.xx.zyjn.decode.encode; + +import com.fr.io.collection.ExportCollection; +import com.fr.io.exporter.AppExporter; +import com.fr.plugin.xx.zyjn.decode.LogUtils; +import com.fr.web.core.ReportSessionIDInfor; +import com.fr.web.core.reserve.DefaultExportExtension; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Author xx + * @Date 2022/3/7 + * @Description + **/ +public class EncodeExportExtensionProvider extends DefaultExportExtension { + + @Override + public ExportCollection createCollection(HttpServletRequest req, HttpServletResponse res, + ReportSessionIDInfor info, String format, String filename, boolean isEmbed) throws Exception { + ExportCollection collection = super.createCollection(req, res, info, format, filename, isEmbed); + LogUtils.debug4plugin("get encode exporter"); + AppExporter exporter = collection.getExporter(); + collection.setExporter(new EncryptExporter(exporter)); + return collection; + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptExporter.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptExporter.java new file mode 100644 index 0000000..38ffcb6 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptExporter.java @@ -0,0 +1,48 @@ +package com.fr.plugin.xx.zyjn.decode.encode; + +import com.fr.io.exporter.AppExporter; +import com.fr.main.workbook.ResultWorkBook; +import com.fr.page.PageSetCreator; +import com.fr.page.PageSetProvider; +import com.fr.web.core.ReportRepositoryDeal; + +import java.io.OutputStream; + +/** + * @Author xx + * @Date 2022/3/7 + * @Description + **/ +public class EncryptExporter implements AppExporter { + + private AppExporter original; + + public EncryptExporter(AppExporter original) { + this.original = original; + } + + @Override + public void export(OutputStream out, ResultWorkBook book) throws Exception { + original.export(new EncryptOutputStream(out), book); + } + + @Override + public void export(OutputStream out, PageSetProvider pageSet) throws Exception { + original.export(new EncryptOutputStream(out), pageSet); + } + + @Override + public void export(OutputStream out, ResultWorkBook book, PageSetCreator creator, ReportRepositoryDeal repo, int[] sheets) throws Exception { + original.export(new EncryptOutputStream(out), book, creator, repo, sheets); + } + + @Override + public void export(OutputStream out, ResultWorkBook book, PageSetProvider pageSet, ReportRepositoryDeal repo, int[] sheets) throws Exception { + original.export(new EncryptOutputStream(out), book, pageSet, repo, sheets); + } + + @Override + public void setVersion(Object o) { + original.setVersion(o); + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptOutputStream.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptOutputStream.java new file mode 100644 index 0000000..45db055 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptOutputStream.java @@ -0,0 +1,190 @@ +package com.fr.plugin.xx.zyjn.decode.encode; + +import cn.net.drm.edi.client.DrmAgent; +import cn.net.drm.edi.client.DrmAgentInf; +import cn.net.drm.io.IOUtils; +import com.fr.general.CommonIOUtils; +import com.fr.plugin.xx.zyjn.decode.DecodeConfig; +import com.fr.plugin.xx.zyjn.decode.LogUtils; +import com.fr.plugin.xx.zyjn.decode.encode.bi.FileUtil; +import org.apache.tools.zip.ZipOutputStream; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import java.io.*; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @Author xx + * @Date 2022/3/3 + * @Description + **/ +public class EncryptOutputStream extends ServletOutputStream { + + private OutputStream original = null; + private boolean closed = false; + private ByteArrayOutputStream crypt = new ByteArrayOutputStream(); + + public EncryptOutputStream(OutputStream original) { + this.original = original; + } + + /** + * 实际加密的代码就在这里实现了 + * + * @param bytes + * @param ignore + * @return + */ + public static byte[] encrypt(byte[] bytes, boolean ignore) { + DecodeConfig config = DecodeConfig.getInstance(); + /*if (!config.isConfiged()) { + LogUtils.warn("config not finish, not encrypted"); + return bytes; + }*/ + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + byte[] header = new byte[256]; + bis.mark(256); + byte[] encode = bytes; + OutputStream fos = null; + try { + DrmAgentInf instance = null; + instance = DrmAgent.getInstance(); + File tempFile = File.createTempFile("tmp" + UUID.randomUUID().toString(), null); + LogUtils.info("create tmp file path is {}", tempFile.getPath()); + bis.read(header); + bis.reset(); + boolean isEncryptFile = instance.isEncrypted(tempFile); + if (!isEncryptFile) { + if (FileUtil.isArchiveFile(bytes) && !ignore) { + encode = encodeZip(bytes, instance, config); + } else { + //fos = instance.encryptBasicFile(tempFile, config.getOwnerId(), config.getSecretLevelId()); + int len = 0; + byte[] buffer = new byte[1024]; + while ((len = bis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.flush(); + FileInputStream in = new FileInputStream(tempFile); + encode = CommonIOUtils.inputStream2Bytes(in); + tempFile.deleteOnExit(); + } + } + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } finally { + IOUtils.closeQuietly(bis); + IOUtils.closeQuietly(fos); + + } + //假设逆序就是加密了 + return encode; + } + + private static byte[] encodeZip(byte[] bytes, DrmAgentInf instance, DecodeConfig config) { + File zipFile = null, encodeZipFile = null; + File unzipFileFolder = FileUtil.createTempFolder("tmp_unzip" + UUID.randomUUID().toString()); + File zipFileFolder = FileUtil.createTempFolder("tmp_zip" + UUID.randomUUID().toString()); + try { + encodeZipFile = FileUtil.createTempFile(); + zipFile = FileUtil.createTempFile(); + //先缓存文件 + com.fr.general.IOUtils.copyBinaryTo(new ByteArrayInputStream(bytes), new FileOutputStream(zipFile)); + LogUtils.debug4plugin("save zip file path:{} size is {}", zipFile.getAbsolutePath(), zipFile.getTotalSpace()); + //再解压出来 + com.fr.general.IOUtils.unzip(zipFile, unzipFileFolder.getAbsolutePath(),"GBK"); + LogUtils.debug4plugin("unzip file path is {} file list is {} unzip size is {}", unzipFileFolder.getAbsolutePath(), Stream.of(unzipFileFolder.listFiles()).map(e->e.getName()).collect(Collectors.joining(",")), unzipFileFolder.listFiles().length); + File[] files = unzipFileFolder.listFiles(); + ZipOutputStream zipOutputStream = new ZipOutputStream(encodeZipFile); + zipOutputStream.setEncoding("GBK"); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + //加密复制到另外一个文件夹 + String encode = zipFileFolder + File.separator + file.getName(); + InputStream bis = new FileInputStream(file); + //加密 + OutputStream fos =null;// instance.encryptBasicStream(new FileOutputStream(encode), config.getOwnerId(), config.getSecretLevelId()); + int len = 0; + byte[] buffer = new byte[1024]; + while ((len = bis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.flush(); + IOUtils.closeQuietly(bis); + IOUtils.closeQuietly(fos); + LogUtils.debug4plugin("encode file {} to {}", file.getAbsolutePath(), encode); + } + //zip + com.fr.general.IOUtils.zip(zipOutputStream, zipFileFolder); + zipOutputStream.closeEntry(); + zipOutputStream.close(); + LogUtils.debug4plugin("zip file folder {} to {}", zipFileFolder, encodeZipFile.getAbsolutePath()); + bytes = CommonIOUtils.inputStream2Bytes(new FileInputStream(encodeZipFile)); + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } finally { + if (zipFile != null) { + zipFile.deleteOnExit(); + } + if (encodeZipFile != null) { + encodeZipFile.deleteOnExit(); + } + unzipFileFolder.deleteOnExit(); + zipFileFolder.deleteOnExit(); + zipFile.deleteOnExit(); + } + return bytes; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + throw new IOException("Cannot write to a closed output stream"); + } + crypt.write(b); + } + + @Override + public void close() throws IOException { + if (!closed) { + byte[] bytes = crypt.toByteArray(); + bytes = encrypt(bytes, false); + original.write(bytes); + original.flush(); + original.close(); + closed = true; + } + } + + @Override + public void flush() throws IOException { + crypt.flush(); + } + + @Override + public void write(byte[] data) throws IOException { + write(data, 0, data.length); + } + + @Override + public void write(byte[] data, int off, int len) throws IOException { + if (closed) { + throw new IOException("Cannot write to a closed output stream"); + } + crypt.write(data, off, len); + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener listener) { + + } + +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptResponse.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptResponse.java new file mode 100644 index 0000000..18e2be1 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/EncryptResponse.java @@ -0,0 +1,25 @@ +package com.fr.plugin.xx.zyjn.decode.encode; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.IOException; + +/** + * @Author xx + * @Date 2022/3/3 + * @Description + **/ +public class EncryptResponse extends HttpServletResponseWrapper { + + public EncryptResponse(HttpServletResponse response) { + super(response); + } + + @Override + public ServletOutputStream getOutputStream()throws IOException { + ServletOutputStream original = super.getOutputStream(); + return new EncryptOutputStream(original); + } + +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/EncodeFilter.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/EncodeFilter.java new file mode 100644 index 0000000..dab0a7f --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/EncodeFilter.java @@ -0,0 +1,71 @@ +package com.fr.plugin.xx.zyjn.decode.encode.bi; + +import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; +import com.fr.decision.webservice.v10.config.ConfigService; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.xx.zyjn.decode.LogUtils; +import com.fr.plugin.xx.zyjn.decode.util.GZIPUtils; +import com.fr.stable.StringUtils; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Author xx + * @Date 2022/3/3 + * @Description + **/ +public class EncodeFilter extends AbstractGlobalRequestFilterProvider { + @Override + public String filterName() { + return "encode"; + } + + @Override + public String[] urlPatterns() { + String servletPathName = "decision"; + try { + servletPathName = ConfigService.getInstance().getBasicParam().getServletPathName(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return new String[]{ + "/" + servletPathName + "/v5/design/report/data/export", + "/" + servletPathName + "/v5/design/report/data/global/export/pdf", + "/" + servletPathName + "/v5/design/report/data/global/export/excel", + + }; + } + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + String method = req.getMethod(); + if ("POST".equalsIgnoreCase(method)) { + String uri = req.getRequestURI(); + String query = req.getQueryString(); + LogUtils.debug4plugin("BI export url {}",uri + "?" + query); + ResponseWrapperImpl wrapper = new ResponseWrapperImpl(res); + filterChain.doFilter(req, wrapper); + FileUtil.handle(res.getOutputStream(), os -> { + byte[] bytes = wrapper.getDataStream(); + if (StringUtils.contains(wrapper.getHeader("Content-Encoding"), "gzip")) { + FineLoggerFactory.getLogger().info("current url {} is gzip", getUrl(req)); + bytes = GZIPUtils.decompress(bytes); + } + os.write(bytes); + os.flush(); + }, req,res); + } + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } + } + + private String getUrl(HttpServletRequest req) { + String uri = req.getRequestURI(); + String query = req.getQueryString(); + return StringUtils.isNotBlank(query) ? uri + "?" + query : uri; + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ExportFunc.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ExportFunc.java new file mode 100644 index 0000000..7042614 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ExportFunc.java @@ -0,0 +1,18 @@ +package com.fr.plugin.xx.zyjn.decode.encode.bi; + +import java.io.OutputStream; + +/** + * @author xx + * @date 2020/11/20 + */ +@FunctionalInterface +public interface ExportFunc { + /** + * 写入到临时输出流 + * + * @param os 临时输出流 + * @return void + **/ + void export(OutputStream os) throws Exception; +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/FileUtil.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/FileUtil.java new file mode 100644 index 0000000..da646ad --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/FileUtil.java @@ -0,0 +1,169 @@ +package com.fr.plugin.xx.zyjn.decode.encode.bi; + +import com.fr.general.CommonIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.xx.zyjn.decode.LogUtils; +import com.fr.plugin.xx.zyjn.decode.encode.EncryptOutputStream; +import com.fr.plugin.xx.zyjn.decode.util.GZIPUtils; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.io.IOUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.util.Arrays; +import java.util.UUID; + +/** + * @author xx + * @date 2020/11/20 + */ +public class FileUtil { + + /** + * 文件加密之后再写入指定的输出流 + * + * @param file + * @param stream + * @return void + **/ + public static void handle(File file, OutputStream stream, HttpServletRequest req, HttpServletResponse res) { + InputStream is = null; + try { + FileInputStream inputStream = new FileInputStream(file); + byte[] encodeFile = encodeFile(CommonIOUtils.inputStream2Bytes(inputStream), req); + is = new ByteArrayInputStream(encodeFile); + byte[] bytes = CommonIOUtils.inputStream2Bytes(is); + if (StringUtils.contains(res.getHeader("Content-Encoding"), "gzip")) { + bytes = GZIPUtils.compress(bytes); + } + IOUtils.copyLarge(new ByteArrayInputStream(bytes), stream); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + if (stream != null) { + try { + stream.flush(); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + } + } + + private static byte[] encodeFile(byte[] file, HttpServletRequest req) { + try { + return EncryptOutputStream.encrypt(file,true); + } catch (Exception e) { + LogUtils.error(e.getMessage(), e); + } + return file; + } + + /** + * 加密导出文件后写入原始输出流中 + * 回调函数式处理 + * + * @param outputStream 原始输出流 + * @param func 临时输出流处理 + * @return void + **/ + public static void handle(OutputStream outputStream, ExportFunc func, HttpServletRequest req, HttpServletResponse res) { + File tempFile = null; + OutputStream os = null; + //写入到临时文件 + try { + tempFile = FileUtil.createTempFile(); + os = new FileOutputStream(tempFile); + //直接写到临时文件里边 + func.export(os); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + } + + //处理临时文件,写到原始输出流 + try { + handle(tempFile, outputStream, req,res);//临时加密后写到原始输出流中 + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } finally { + if (tempFile != null) { + deleteFile(tempFile);//最后删除临时文件 + } + } + } + + /** + * 创建临时文件 + * + * @return java.io.File + **/ + public static File createTempFile() throws IOException { + return File.createTempFile("tmp" + UUID.randomUUID().toString(), null);//创建一个临时文件 + } + + /** + * 删除文件 + * + * @param file + * @return boolean + * @author Zhanying + **/ + public static boolean deleteFile(File file) { + boolean flag = file.exists() && file.isFile() && file.delete(); + // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 + return flag; + } + + private static byte[] ZIP_HEADER_1 = new byte[]{80, 75, 3, 4}; + + private static byte[] ZIP_HEADER_2 = new byte[]{80, 75, 5, 6}; + + public static boolean isArchiveFile(byte[] file) { + if (file == null) { + return false; + } + boolean isArchive = false; + InputStream input = null; + try { + input = new ByteArrayInputStream(file); + byte[] buffer = new byte[4]; + int length = input.read(buffer, 0, 4); + if (length == 4) { + isArchive = (Arrays.equals(ZIP_HEADER_1, buffer)) || (Arrays.equals(ZIP_HEADER_2, buffer)); + } + } catch (IOException e) { + LogUtils.error(e.getMessage(), e); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + } + } + } + return isArchive; + } + + + public static File createTempFolder(String path) { + File file = new File(System.getProperty("java.io.tmpdir")+File.separator+path); + file.mkdir(); + return file; + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ResponseWrapperImpl.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ResponseWrapperImpl.java new file mode 100644 index 0000000..be50749 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/encode/bi/ResponseWrapperImpl.java @@ -0,0 +1,70 @@ +package com.fr.plugin.xx.zyjn.decode.encode.bi; + +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.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * @author xx + * @date 2020/11/20 + */ +public class ResponseWrapperImpl extends HttpServletResponseWrapper { + ByteArrayOutputStream output; + FilterServletOutputStream filterOutput; + + public ResponseWrapperImpl(HttpServletResponse response) { + super(response); + output = new ByteArrayOutputStream(); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + if (filterOutput == null) { + filterOutput = new FilterServletOutputStream(output); + } + return filterOutput; + } + + public byte[] getDataStream() { + return output.toByteArray(); + } + + class FilterServletOutputStream extends ServletOutputStream { + DataOutputStream output; + + public FilterServletOutputStream(OutputStream output) { + this.output = new DataOutputStream(output); + } + + @Override + public void write(int arg0) throws IOException { + output.write(arg0); + } + + @Override + public void write(byte[] arg0, int arg1, int arg2) throws IOException { + output.write(arg0, arg1, arg2); + } + + @Override + public void write(byte[] arg0) throws IOException { + output.write(arg0); + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + } +} diff --git a/src/main/java/com/fr/plugin/xx/zyjn/decode/util/GZIPUtils.java b/src/main/java/com/fr/plugin/xx/zyjn/decode/util/GZIPUtils.java new file mode 100644 index 0000000..1e0bf40 --- /dev/null +++ b/src/main/java/com/fr/plugin/xx/zyjn/decode/util/GZIPUtils.java @@ -0,0 +1,89 @@ +package com.fr.plugin.xx.zyjn.decode.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * @Author xx + * @Date 2020/9/15 + * @Description + **/ +public class GZIPUtils { + + + + /** + * 数据压缩传输 + * + * @param is + * @param os + * @throws Exception + */ + public static void compressTransfe(byte[] bytes, OutputStream out) throws IOException { + GZIPOutputStream gos = null; + try { + gos = new GZIPOutputStream(out); + gos.write(bytes); + gos.finish(); + gos.flush(); + } finally{ + if(gos != null){ + gos.close(); + } + } + } + + /** + * 数据压缩 + * + * @param is + * @param os + * @throws Exception + */ + public static byte[] compress(byte[] bytes) throws IOException { + ByteArrayOutputStream out = null; + GZIPOutputStream gos = null; + try { + out = new ByteArrayOutputStream(); + gos = new GZIPOutputStream(out); + gos.write(bytes); + gos.finish(); + gos.flush(); + } finally{ + if(gos != null){ + gos.close(); + } + if(out != null){ + out.close(); + } + } + return out.toByteArray(); + } + + /** + * 数据解压 + * + * @param in + * @return + * @throws IOException + */ + public static byte[] decompress(byte[] bytes) throws IOException { + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + GZIPInputStream gin = new GZIPInputStream(in); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int count; + byte data[] = new byte[1024]; + while ((count = gin.read(data, 0, 1024)) != -1) { + out.write(data, 0, count); + } + out.flush(); + out.close(); + gin.close(); + return out.toByteArray(); + } + +}