From 86e0040303be3a9d7e8df5bfcf6cb8029427d6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E7=90=A6=E6=B6=9B?= Date: Fri, 24 Nov 2023 18:19:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=E5=8A=A0?= =?UTF-8?q?=E8=A7=A3=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/controller/CommonController.java | 34 ++- .../config/ContentCachingWrapperFilter.java | 87 ++++++++ .../download/config/DownloadInterceptor.java | 60 ++++++ .../download/config/InterceptorConfig.java | 25 +++ .../service/DownloadDecryptFileService.java | 23 +++ .../impl/DownloadDecryptFileServiceImpl.java | 195 ++++++++++++++++++ .../encrypt/controller/EncrygtController.java | 47 ----- .../modules/docking/utils/IntekeyUtils.java | 5 +- .../src/main/resources/application-dev.yml | 22 ++ 9 files changed, 440 insertions(+), 58 deletions(-) create mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/ContentCachingWrapperFilter.java create mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/DownloadInterceptor.java create mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/InterceptorConfig.java create mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/DownloadDecryptFileService.java create mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/impl/DownloadDecryptFileServiceImpl.java delete mode 100644 laws-modules-docking/src/main/java/com/jero/modules/docking/encrypt/controller/EncrygtController.java diff --git a/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java b/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java index 8088493f..90560c9d 100644 --- a/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java +++ b/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java @@ -137,27 +137,43 @@ public class CommonController { } /** - * 预览图片&下载文件 + * 下载文件 * * @param id 传入文件id * @param request * @param response */ @GetMapping(value = "/download/{id}") + public void download(@PathVariable String id,HttpServletRequest request, HttpServletResponse response) { + downloadAndView(id, response); + } + + /** + * 预览图片 + * + * @param id 传入文件id + * @param request + * @param response + */ + @GetMapping(value = "/view/{id}") public void view(@PathVariable String id,HttpServletRequest request, HttpServletResponse response) { + downloadAndView(id, response); + } + + private void downloadAndView(@PathVariable String id, HttpServletResponse response) { // 查询数据表数据是否存在 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(OSSFile::getId,id); + queryWrapper.eq(OSSFile::getId, id); OSSFile ossFile = ossFileService.getOne(queryWrapper); - if( null == ossFile){ + if (null == ossFile) { throw new JeroBootException("文件不存在.."); } String fileUrl = ossFile.getUrl(); - if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){ + if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) { //本地下载 String filePath = uploadpath + File.separator + fileUrl; File file = new File(filePath); - if(!file.exists()){ + if (!file.exists()) { response.setStatus(404); throw new JeroBootException("文件不存在.."); } @@ -173,12 +189,12 @@ public class CommonController { outputStream.write(buf, 0, len); } response.flushBuffer(); - }catch (Exception e){ + } catch (Exception e) { log.error(FILE_VIEW_ERROR + e.getMessage()); response.setStatus(404); e.printStackTrace(); } - }else if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + } else if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) { // minio 下载 // 通过MinioUtil查询时 只需要桶后面的路径 String minioUrl = MinioUtil.getMinioUrl(); @@ -193,14 +209,14 @@ public class CommonController { ) { byte[] buf = new byte[1024]; int len; - if(inputStream==null) { + if (inputStream == null) { return; } while ((len = inputStream.read(buf)) > 0) { outputStream.write(buf, 0, len); } response.flushBuffer(); - }catch (Exception e){ + } catch (Exception e) { log.error(FILE_VIEW_ERROR + e.getMessage()); response.setStatus(404); e.printStackTrace(); diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/ContentCachingWrapperFilter.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/ContentCachingWrapperFilter.java new file mode 100644 index 00000000..a179947a --- /dev/null +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/ContentCachingWrapperFilter.java @@ -0,0 +1,87 @@ +package com.jero.modules.docking.download.config; + +import org.apache.commons.io.IOUtils; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; +import org.springframework.web.util.WebUtils; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * @author lqt + * @version 1.0 + * @date 2023/11/24 11:36 + */ +//@Component +public class ContentCachingWrapperFilter extends OncePerRequestFilter implements Ordered { + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE - 10; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + // 包装流,可重复读取 + if (!(request instanceof ContentCachingRequestWrapper)) { + request = new ContentCachingRequestWrapper(request); + } + if (!(response instanceof ContentCachingResponseWrapper)) { + response = new ContentCachingResponseWrapper(response); + } + + filterChain.doFilter(request, response); + updateResponse(response); + } + + /** + * 更新响应(不操作这一步,会导致接口响应空白) + * + * @param response 响应对象 + * @throws IOException / + */ + public static void updateResponse(HttpServletResponse response) throws IOException { + ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); + Objects.requireNonNull(responseWrapper).copyBodyToResponse(); + } + + /** + * 获取请求体 + * + * @param request 请求对象 + * @return 请求体 + */ + public static String getRequestBody(HttpServletRequest request) throws IOException { + String requestBody = ""; + ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); + if (wrapper != null) { + requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.toString()); + } + return requestBody; + } + + /** + * 获取响应体 + * + * @param response 响应对象 + * @return 响应体 + */ + public static String getResponseBody(HttpServletResponse response) throws IOException { + String responseBody = ""; + ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); + if (wrapper != null) { + responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.toString()); + } + return responseBody; + } +} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/DownloadInterceptor.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/DownloadInterceptor.java new file mode 100644 index 00000000..a53be553 --- /dev/null +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/DownloadInterceptor.java @@ -0,0 +1,60 @@ +package com.jero.modules.docking.download.config; + +import com.jero.modules.docking.download.service.DownloadDecryptFileService; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author lqt + * @version 1.0 + * @date 2023/11/23 18:20 + */ +@Component +public class DownloadInterceptor implements HandlerInterceptor{ + + @Resource + private DownloadDecryptFileService downloadDecryptFileService; + + public DownloadInterceptor(DownloadDecryptFileService downloadDecryptFileService) { + this.downloadDecryptFileService = downloadDecryptFileService; + } + + /** + * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器 + * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应 + * @throws Exception + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { +// HandlerMethod handlerMethod = (HandlerMethod) handler; +// Method method = handlerMethod.getMethod(); +// String methodName = method.getName(); +// System.out.println("====拦截到了方法:"+methodName+",preHandle===="); + // other + return true; + } + + /** + * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null + */ + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { +// downloadDecryptFileService.downloadDecryptFile(request,response,handler); + } + + /** + *整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中 + */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { +// downloadDecryptFileService.downloadDecryptFile(request,response,handler); + } +} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/InterceptorConfig.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/InterceptorConfig.java new file mode 100644 index 00000000..74cf99b8 --- /dev/null +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/config/InterceptorConfig.java @@ -0,0 +1,25 @@ +package com.jero.modules.docking.download.config; + +import com.jero.modules.docking.download.service.DownloadDecryptFileService; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; + +/** + * @author lqt + * @version 1.0 + * @date 2023/11/23 18:21 + */ +@Configuration +public class InterceptorConfig implements WebMvcConfigurer { + @Resource + private DownloadDecryptFileService downloadDecryptFileService; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new DownloadInterceptor(downloadDecryptFileService)).addPathPatterns("/**"); + } + +} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/DownloadDecryptFileService.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/DownloadDecryptFileService.java new file mode 100644 index 00000000..e6ae8a8a --- /dev/null +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/DownloadDecryptFileService.java @@ -0,0 +1,23 @@ +package com.jero.modules.docking.download.service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author lqt + * @version 1.0 + * @date 2023/11/24 9:04 + */ +public interface DownloadDecryptFileService { + + /** + * + * @author LQT + * @date 2023/11/24 9:06 + * @param request + * @param response + * @param handler + * @return void + */ + void downloadDecryptFile(HttpServletRequest request, HttpServletResponse response, Object handler); +} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/impl/DownloadDecryptFileServiceImpl.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/impl/DownloadDecryptFileServiceImpl.java new file mode 100644 index 00000000..492d7cb4 --- /dev/null +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/download/service/impl/DownloadDecryptFileServiceImpl.java @@ -0,0 +1,195 @@ +package com.jero.modules.docking.download.service.impl; + +import com.jero.common.api.vo.ResultCommon; +import com.jero.common.exception.JeroBootException; +import com.jero.common.system.vo.LoginUser; +import com.jero.modules.docking.download.config.ContentCachingWrapperFilter; +import com.jero.modules.docking.download.service.DownloadDecryptFileService; +import com.jero.modules.docking.utils.IntekeyUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.entity.ContentType; +import org.apache.shiro.SecurityUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Service; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * @author lqt + * @version 1.0 + * @date 2023/11/24 9:04 + */ +@Slf4j +@Service +public class DownloadDecryptFileServiceImpl implements DownloadDecryptFileService { + + @Value("#{'${download.download_url}'.split(',')}") + private List downloadUrl; + + @Value("#{'${download.view_url}'.split(',')}") + private List viewUrl; + + /** + * 管理员角色 + */ + @Value("#{'${download.admin_role_code}'.split(',')}") + private List adminRoleCode; + + /** + * 加密 + */ + @Value("#{'${download.encrypt.url}'}") + private String encryptUrl; + @Value("#{'${download.encrypt.app_code}'}") + private String encryptAppCode; + @Value("#{'${download.encrypt.secret_key}'}") + private String encryptSecretKey; + + /** + * 解密 + */ + @Value("#{'${download.decrypt.url}'}") + private String decryptUrl; + @Value("#{'${download.decrypt.app_code}'}") + private String decryptAppCode; + @Value("#{'${download.decrypt.secret_key}'}") + private String decryptSecretKey; + + /** + * 研发域 + */ + @Value("#{'${download.scope_dev}'}") + private int scopeDev; + /** + * 办公域 + */ + @Value("#{'${download.scope_work}'}") + private int scopeWork; + + + @Override + public void downloadDecryptFile(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 判断返回值是否为文件流,否则其他接口返回值会变更成字符串,导致前端无法解析 + String responseBody = null; + HandlerMethod handlerMethod = (HandlerMethod) handler; + try { + String returnType = handlerMethod.getMethod().getReturnType().getName(); + if(!Objects.equals(returnType,"void") && !Objects.equals(returnType,"org.springframework.web.servlet.ModelAndView")){ + return; + } + responseBody = ContentCachingWrapperFilter.getResponseBody(response); + if(StringUtils.isBlank(responseBody)){ + return; + } + } catch (IOException e) { + e.printStackTrace(); + } + + MultipartFile file; + try { + InputStream inputStream = new ByteArrayInputStream(responseBody.getBytes(StandardCharsets.UTF_8)); + file = new MockMultipartFile(ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream); + } catch (IOException e) { + e.printStackTrace(); + throw new JeroBootException(ResultCommon.ERROR); + } + // 访问路径 + String str = request.getRequestURI(); + + boolean b = false; + for (String s : viewUrl) { + if(s.contains(str)){ + b = true; + break; + } + } + + boolean b1 = false; + for (String s : downloadUrl) { + if(s.contains(str)){ + b1 = true; + break; + } + } + + if(b){ + // 预览,进行解密 + try (InputStream is = IntekeyUtils.DecryptFile(decryptUrl, decryptAppCode, decryptSecretKey, null, file)){ + writeResponse(response,is); + }catch (Exception e){ + throw new JeroBootException(ResultCommon.ERROR); + } + }else if(b1){ + // 下载文件 先解密,判断组织域,加密 + try (InputStream is = IntekeyUtils.DecryptFile(decryptUrl, decryptAppCode, decryptSecretKey, null, file)){ + writeResponse(response,is); + }catch (Exception e){ + throw new JeroBootException(ResultCommon.ERROR); + } + int scope = getScope(); + // 加密 + try (InputStream is = IntekeyUtils.DecryptFile(encryptUrl, encryptAppCode, encryptSecretKey, scope, file)){ + writeResponse(response,is); + }catch (Exception e){ + throw new JeroBootException(ResultCommon.ERROR); + } + return; + }else{ + // excel 判断组织域,加密 + int scope = getScope(); + // 加密 + try (InputStream is = IntekeyUtils.DecryptFile(encryptUrl, encryptAppCode, encryptSecretKey, scope, file)){ + writeResponse(response,is); + }catch (Exception e){ + throw new JeroBootException(ResultCommon.ERROR); + } + } + } + + private int getScope() { +// 角色信息中存在 【系统管理员】或者 【标准管理员】即加为研发密,其他情况都是OA密 +// 研发密比较高级(加密需要传参) +// 研发域 scope = 51 +// 办公密 scope = 52 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List listRoleCode = Arrays.asList(sysUser.getRoleIds().split(",")); + if(CollectionUtils.isEmpty(listRoleCode)){ + throw new JeroBootException(ResultCommon.ERROR); + } + int scope = scopeWork; + long l = listRoleCode.stream().filter(o->adminRoleCode.contains(o)).count(); + if(l > 0){ + scope = scopeDev; + } + return scope; + } + + private void writeResponse(HttpServletResponse response,InputStream is) { + try (OutputStream outputStream = response.getOutputStream()) { + byte[] buf = new byte[1024]; + int len; + while ((len = is.read(buf)) > 0) { + outputStream.write(buf, 0, len); + } + response.flushBuffer(); + }catch (Exception e){ + log.error(e.getMessage()); + response.setStatus(404); + e.printStackTrace(); + } + } +} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/encrypt/controller/EncrygtController.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/encrypt/controller/EncrygtController.java deleted file mode 100644 index 7e69be48..00000000 --- a/laws-modules-docking/src/main/java/com/jero/modules/docking/encrypt/controller/EncrygtController.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.jero.modules.docking.encrypt.controller; - -import com.jero.modules.docking.utils.IntekeyUtils; -import io.swagger.annotations.Api; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author lqt - * @version 1.0 - * @date 2023/11/7 15:22 - */ -@Slf4j -@Api(tags = "加解密附件") -@RequestMapping("/encrypt") -@RestController -public class EncrygtController { - - /** - * 下载附件 - * - */ - @PostMapping(value = "/download") - public void view(MultipartFile file,String url, String appCode, String secretKey,String scope, HttpServletResponse response) { - try (InputStream inputStream = IntekeyUtils.DecryptFile(url, appCode, secretKey, scope, file); - OutputStream outputStream = response.getOutputStream() - ) { - byte[] buf = new byte[1024]; - int len; - while ((len = inputStream.read(buf)) > 0) { - outputStream.write(buf, 0, len); - } - response.flushBuffer(); - }catch (Exception e){ - log.error(e.getMessage()); - response.setStatus(404); - e.printStackTrace(); - } - } -} diff --git a/laws-modules-docking/src/main/java/com/jero/modules/docking/utils/IntekeyUtils.java b/laws-modules-docking/src/main/java/com/jero/modules/docking/utils/IntekeyUtils.java index 55dda23d..cedbdd2f 100644 --- a/laws-modules-docking/src/main/java/com/jero/modules/docking/utils/IntekeyUtils.java +++ b/laws-modules-docking/src/main/java/com/jero/modules/docking/utils/IntekeyUtils.java @@ -8,6 +8,7 @@ import org.springframework.http.*; import org.springframework.util.DigestUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; @@ -32,14 +33,14 @@ public class IntekeyUtils { * @return */ //todo 这个方法需要写在laws-modules-docking 里面 可以新建一个文件夹是encrypt - public static InputStream DecryptFile(String url, String appCode, String secretKey, String scope, MultipartFile multipartFile) { + public static InputStream DecryptFile(String url, String appCode, String secretKey, Integer scope, MultipartFile multipartFile) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); logger.info("解密时间:" + format.format(new Date())); //设置校验 Long time = System.currentTimeMillis(); String sign = DigestUtils.md5DigestAsHex((secretKey + time).getBytes()); - url = url + "?appCode=" + appCode + "&time=" + time + "&sign=" + sign + "&scope=" + scope; + url = url + "?appCode=" + appCode + "&time=" + time + "&sign=" + sign + (Objects.isNull(scope) ? "" : "&scope=" + scope); File file = transferToFile(multipartFile); RestTemplate restTemplate = new RestTemplate(); //设置请求头 diff --git a/laws-single-startup/src/main/resources/application-dev.yml b/laws-single-startup/src/main/resources/application-dev.yml index e9870cb2..591a83eb 100644 --- a/laws-single-startup/src/main/resources/application-dev.yml +++ b/laws-single-startup/src/main/resources/application-dev.yml @@ -429,3 +429,25 @@ draftDepartName: 起草部门 # 管理员权限roleCode,多填用英文逗号拼接 adminRoleCode: admin + +# 文件下载加解密 +download: + # 文件下载、预览拦截路径,多个以逗号分隔 + download_url: /sys/common/download/ + view_url: /sys/common/view/ + # 管理员角色 + admin_role_code: admin,BZGLY + # 加密参数 + encrypt: + url: http://10.2.159.90/intekey/encrypteFile + app_code: SRMS + secret_key: SRMS1234.. + # 解密参数 + decrypt: + url: http://10.2.159.90/intekey/file + app_code: SRMS + secret_key: SRMS1234.. + # 研发域 + scope_dev: 51 + # 办公域 + scope_work: 52