diff --git a/src/main/java/cn/lihongjie/coal/filter/SignFilter.java b/src/main/java/cn/lihongjie/coal/filter/SignFilter.java index af150a44..74bebdc6 100644 --- a/src/main/java/cn/lihongjie/coal/filter/SignFilter.java +++ b/src/main/java/cn/lihongjie/coal/filter/SignFilter.java @@ -34,7 +34,6 @@ import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.*; import java.util.concurrent.TimeUnit; import javax.crypto.Mac; @@ -54,159 +53,165 @@ public class SignFilter extends OncePerRequestFilter { @Autowired RedisTemplate redisTemplate; + private static String getFieldFromHeaderOrQs(HttpServletRequest request, String name) { + return StringUtils.defaultIfEmpty(request.getHeader(name), request.getParameter(name)); + } + @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_REQUEST_SIGN)) { - StopWatch stopWatch = new StopWatch(); - - stopWatch.start("基本请求头"); - - StringBuilder sb = new StringBuilder(); - - // 请求方法 - - sb.append(StringUtils.defaultString(request.getMethod().toUpperCase())); - sb.append(StringUtils.defaultString("\n")); - - // 请求地址 - sb.append( - StringUtils.defaultString( - ObjectUtils.defaultIfNull( - request.getHeader(Constants.HTTP_HEADER_CLIENT_URL), - request.getRequestURI()))); - sb.append(StringUtils.defaultString("\n")); - - // 请求参数 - sb.append(StringUtils.defaultString(request.getQueryString())); - sb.append(StringUtils.defaultString("\n")); - - // 请求头 - - String token = - ObjectUtils.defaultIfNull( - request.getHeader(Constants.HTTP_HEADER_TOKEN), "anonymous"); - String ts = request.getHeader(Constants.HTTP_HEADER_TS); - if (StringUtils.isEmpty(ts)) { - RequestUtils.writeResponse( - new BizException(Constants.HTTP_HEADER_TS + " 请求头缺失"), response); - return; - } - - String random = request.getHeader(Constants.HTTP_HEADER_CLIENT_RANDOM); - if (StringUtils.isEmpty(random)) { - RequestUtils.writeResponse( - new BizException(Constants.HTTP_HEADER_CLIENT_RANDOM + " 请求头缺失"), response); - return; - } - long tsi = 0; - - try { - - tsi = Long.parseLong(ts); - } catch (Exception e) { - RequestUtils.writeResponse( - new BizException(Constants.HTTP_HEADER_TS + " 请求头格式错误"), response); - return; - } - - long current = System.currentTimeMillis(); - - if (Math.abs(tsi - current) > TIME_DIFF_MS) { - RequestUtils.writeResponse( - new BizException( - Constants.HTTP_HEADER_TS - + " 客户端时间误差过大,请校准时间. 服务器时间为: " - + LocalDateTime.now()), - response); - return; - } - - sb.append(StringUtils.defaultString(token)); - - sb.append(StringUtils.defaultString("\n")); - - sb.append(StringUtils.defaultString(ts)); - - sb.append(StringUtils.defaultString("\n")); - - sb.append(StringUtils.defaultString(random)); - - sb.append(StringUtils.defaultString("\n")); - - stopWatch.stop(); - - stopWatch.start("sha256Hex"); - - String sha256Hex; - MediaType mediaType = - MediaType.parseMediaType( - ObjectUtils.defaultIfNull( - request.getContentType(), - MediaType.APPLICATION_OCTET_STREAM_VALUE)); - // 文件上传不对body进行签名 - if (mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)) { - - sha256Hex = DigestUtils.sha256Hex("").toUpperCase(); - - } else { - sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase(); - } - - sb.append(StringUtils.defaultString(sha256Hex)); - sb.append(StringUtils.defaultString("\n")); - - stopWatch.stop(); - - stopWatch.start("签名"); - - Mac mac = - HmacUtils.getInitializedMac( - HmacAlgorithms.HMAC_SHA_256, token.getBytes(StandardCharsets.UTF_8)); - mac.update(sb.toString().getBytes(StandardCharsets.UTF_8)); - String sign = Hex.encodeHexString(mac.doFinal()).toUpperCase(); - - stopWatch.stop(); - - String clientSign = request.getHeader(Constants.HTTP_HEADER_CLIENT_SIGN); - - if (!sign.equals(clientSign)) { - log.debug("key: {} \ndata:{}", token, sb); - log.warn("签名错误: {} {}", clientSign, sign); - sysLogService.saveSysLog(request, "签名模块", "签名错误", clientSign + " " + sign); - RequestUtils.writeResponse( - new BizException(Constants.HTTP_HEADER_CLIENT_SIGN + " 请求头签名错误"), response); - return; - } - - stopWatch.start("重复请求"); - String cr = - redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random); - - if (StringUtils.equals(cr, "1")) { - sysLogService.saveSysLog(request, "签名模块", "重复请求", ""); - RequestUtils.writeResponse(new BizException("请求已过期, 请刷新页面重试"), response); - return; - } else { - redisTemplate - .opsForValue() - .set( - Constants.CACHE_CLIENT_RANDOM_PREFIX + random, - "1", - TIME_DIFF_MS, - TimeUnit.MILLISECONDS); - } - - stopWatch.stop(); - - if (request instanceof CacheBodyRequestWrapper cacheBodyRequestWrapper) { - cacheBodyRequestWrapper.reset(); - } - - log.debug("签名验证通过: {}", stopWatch.prettyPrint()); + if (!sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_REQUEST_SIGN)) { + doFilter(request, response, filterChain); + return; } + StopWatch stopWatch = new StopWatch(); + + stopWatch.start("基本请求头"); + + StringBuilder sb = new StringBuilder(); + + // 请求方法 + + sb.append(StringUtils.defaultString(request.getMethod().toUpperCase())); + sb.append(StringUtils.defaultString("\n")); + + // 请求地址 + sb.append( + StringUtils.defaultString( + ObjectUtils.defaultIfNull( + getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_URL), + request.getRequestURI()))); + sb.append(StringUtils.defaultString("\n")); + + // 请求参数 + sb.append(StringUtils.defaultString(request.getQueryString())); + sb.append(StringUtils.defaultString("\n")); + + // 请求头 + + String token = + ObjectUtils.defaultIfNull( + getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_TOKEN), "anonymous"); + String ts = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_TS); + if (StringUtils.isEmpty(ts)) { + RequestUtils.writeResponse( + new BizException(Constants.HTTP_HEADER_TS + " 请求头缺失"), response); + return; + } + + String random = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_RANDOM); + if (StringUtils.isEmpty(random)) { + RequestUtils.writeResponse( + new BizException(Constants.HTTP_HEADER_CLIENT_RANDOM + " 请求头缺失"), response); + return; + } + long tsi = 0; + + try { + + tsi = Long.parseLong(ts); + } catch (Exception e) { + RequestUtils.writeResponse( + new BizException(Constants.HTTP_HEADER_TS + " 请求头格式错误"), response); + return; + } + + long current = System.currentTimeMillis(); + + if (Math.abs(tsi - current) > TIME_DIFF_MS) { + RequestUtils.writeResponse( + new BizException( + Constants.HTTP_HEADER_TS + + " 客户端时间误差过大,请校准时间. 服务器时间为: " + + LocalDateTime.now()), + response); + return; + } + + sb.append(StringUtils.defaultString(token)); + + sb.append(StringUtils.defaultString("\n")); + + sb.append(StringUtils.defaultString(ts)); + + sb.append(StringUtils.defaultString("\n")); + + sb.append(StringUtils.defaultString(random)); + + sb.append(StringUtils.defaultString("\n")); + + stopWatch.stop(); + + stopWatch.start("sha256Hex"); + + String sha256Hex; + MediaType mediaType = + MediaType.parseMediaType( + ObjectUtils.defaultIfNull( + request.getContentType(), + MediaType.APPLICATION_OCTET_STREAM_VALUE)); + // 文件上传不对body进行签名 + if (mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)) { + + sha256Hex = DigestUtils.sha256Hex("").toUpperCase(); + + } else { + sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase(); + } + + sb.append(StringUtils.defaultString(sha256Hex)); + sb.append(StringUtils.defaultString("\n")); + + stopWatch.stop(); + + stopWatch.start("签名"); + + Mac mac = + HmacUtils.getInitializedMac( + HmacAlgorithms.HMAC_SHA_256, token.getBytes(StandardCharsets.UTF_8)); + mac.update(sb.toString().getBytes(StandardCharsets.UTF_8)); + String sign = Hex.encodeHexString(mac.doFinal()).toUpperCase(); + + stopWatch.stop(); + + String clientSign = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_SIGN); + + if (!sign.equals(clientSign)) { + log.debug("key: {} \ndata:{}", token, sb); + log.warn("签名错误: {} {}", clientSign, sign); + sysLogService.saveSysLog(request, "签名模块", "签名错误", clientSign + " " + sign); + RequestUtils.writeResponse( + new BizException(Constants.HTTP_HEADER_CLIENT_SIGN + " 请求头签名错误"), response); + return; + } + + stopWatch.start("重复请求"); + String cr = + redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random); + + if (StringUtils.equals(cr, "1")) { + sysLogService.saveSysLog(request, "签名模块", "重复请求", ""); + RequestUtils.writeResponse(new BizException("请求已过期, 请刷新页面重试"), response); + return; + } else { + redisTemplate + .opsForValue() + .set( + Constants.CACHE_CLIENT_RANDOM_PREFIX + random, + "1", + TIME_DIFF_MS, + TimeUnit.MILLISECONDS); + } + + stopWatch.stop(); + + if (request instanceof CacheBodyRequestWrapper cacheBodyRequestWrapper) { + cacheBodyRequestWrapper.reset(); + } + + log.debug("签名验证通过: {}", stopWatch.prettyPrint()); doFilter(request, response, filterChain); } diff --git a/src/main/java/cn/lihongjie/coal/netDisk/controller/NetDiskController.java b/src/main/java/cn/lihongjie/coal/netDisk/controller/NetDiskController.java index 9695e0e7..adefca36 100644 --- a/src/main/java/cn/lihongjie/coal/netDisk/controller/NetDiskController.java +++ b/src/main/java/cn/lihongjie/coal/netDisk/controller/NetDiskController.java @@ -26,13 +26,39 @@ import java.util.List; public class NetDiskController { @Autowired private NetDiskService service; - - @PostMapping("/getById") public NetDiskDto getById(@RequestBody IdRequest request) { return this.service.getById(request.getId()); } + /** + * id转路径 + * + * @param request + * @return + */ + @PostMapping("/idToPath") + public String idToPath(@RequestBody IdRequest request) { + return this.service.idToPath(request); + } + + /** + * 路径转id + * + * @param request + * @return + */ + @PostMapping("/pathToId") + public String pathToId(@RequestBody IdRequest request) { + return this.service.pathToId(request); + } + + /** + * 列出下级文件和文件夹 + * + * @param request + * @return + */ @PostMapping("/ls") @SysLog(action = "ls", message = "id") public List ls(@RequestBody IdRequest request) { @@ -40,29 +66,71 @@ public class NetDiskController { } + /** + * 列出文件树 + * + * @param request + * @return + */ + @PostMapping("/tree") + public NetDiskTreeDto tree(@RequestBody IdRequest request) { + return this.service.tree(request); + } + + + /** + * 创建文件夹 + * + * @param request + * @return + */ @PostMapping("/createDir") @SysLog(action = "createDir", message = "name") - public NetDiskDto createDir (@RequestBody CreateDirDto request) { + public NetDiskDto createDir(@RequestBody CreateDirDto request) { return this.service.createDir(request); } - + /** + * 批量创建文件夹 + * + * @param request + * @return + */ @PostMapping("/batchCreateDir") @SysLog(action = "createDir", message = "names.join(',')") - public List batchCreateDir (@RequestBody BatchCreateDirDto request) { + public List batchCreateDir(@RequestBody BatchCreateDirDto request) { return this.service.batchCreateDir(request); } + /** + * 预创建文件, 创建文件之前调用, 用于秒传和文件分片进度 + * + * @param request + * @return + */ @PostMapping("/preCreateFile") @SysLog(action = "preCreateFile", message = "name") - - public PreCreateFileDto preCreateFile (@RequestBody CreateFileDto request) { + public PreCreateFileDto preCreateFile(@RequestBody CreateFileDto request) { return this.service.preCreateFile(request); } - + /** + * 创建文件分片 + * + * @param parent + * @param size + * @param sha256 + * @param mineType + * @param multipartFile + * @param ossKey + * @param name + * @param code + * @param remarks + * @param sortKey + * @return + */ @PostMapping("/createFileSlice") - public NetDiskDto createFileSlice ( + public NetDiskDto createFileSlice( @RequestParam("parent") String parent, @RequestParam("size") Long size, @RequestParam("sha256") String sha256, @@ -72,11 +140,8 @@ public class NetDiskController { @RequestParam("name") String name, @RequestParam("code") String code, @RequestParam("remarks") String remarks, - @RequestParam("sortKey") String sortKey + @RequestParam("sortKey") String sortKey) { - - - ) { CreateFileDto request = new CreateFileDto(); request.setParent(parent); request.setSize(size); @@ -87,32 +152,44 @@ public class NetDiskController { request.setName(name); request.setCode(code); request.setRemarks(remarks); - try{ + try { request.setSortKey(Integer.valueOf(sortKey)); - }catch (Exception e){ + } catch (Exception e) { } return this.service.createFileSlice(request); } - + + /** + * 创建文件 + * + * @param parent + * @param size + * @param sha256 + * @param mineType + * @param multipartFile + * @param ossKey + * @param name + * @param code + * @param remarks + * @param sortKey + * @return + */ @PostMapping("/createFile") - public NetDiskDto createFile ( - @RequestParam("parent") String parent, - @RequestParam("size") Long size, - @RequestParam("sha256") String sha256, - @RequestParam("mineType") String mineType, - @RequestParam("multipartFile") MultipartFile multipartFile, - @RequestParam("ossKey") String ossKey, - @RequestParam("name") String name, - @RequestParam("code") String code, - @RequestParam("remarks") String remarks, - @RequestParam("sortKey") String sortKey + public NetDiskDto createFile( + @RequestParam("parent") String parent, + @RequestParam("size") Long size, + @RequestParam("sha256") String sha256, + @RequestParam("mineType") String mineType, + @RequestParam("multipartFile") MultipartFile multipartFile, + @RequestParam("ossKey") String ossKey, + @RequestParam("name") String name, + @RequestParam("code") String code, + @RequestParam("remarks") String remarks, + @RequestParam("sortKey") String sortKey) { - - - ) { CreateFileDto request = new CreateFileDto(); request.setParent(parent); request.setSize(size); @@ -123,51 +200,96 @@ public class NetDiskController { request.setName(name); request.setCode(code); request.setRemarks(remarks); - try{ + try { - request.setSortKey(Integer.valueOf(sortKey)); - }catch (Exception e){ + request.setSortKey(Integer.valueOf(sortKey)); + } catch (Exception e) { } return this.service.createFile(request); } + /** + * 移动 + * + * @param request + * @return + */ @PostMapping("/move") @SysLog(action = "move", message = "srcId + ' -> ' + targetId") - public NetDiskDto move(@RequestBody MoveDto request) { return this.service.move(request); } - + /** + * 复制 + * + * @param request + * @return + */ @PostMapping("/copy") @SysLog(action = "copy", message = "srcId + ' -> ' + targetId") - public NetDiskDto copy (@RequestBody CopyDto request) { + public NetDiskDto copy(@RequestBody CopyDto request) { return this.service.copy(request); } + /** + * 删除 + * + * @param request + * @return + */ @PostMapping("/remove") @SysLog(action = "remove", message = "id") - public Object remove (@RequestBody IdRequest request) { + public Object remove(@RequestBody IdRequest request) { this.service.remove(request); return null; } + /** + * 通过阿里云OSS链接下载文件夹 + * + * @param request + * @param response + */ @PostMapping("/downloadDir") @SysLog(action = "downloadDir", message = "id") - public void remove (@RequestBody IdRequest request, HttpServletResponse response) { + public void downloadDir(@RequestBody IdRequest request, HttpServletResponse response) { this.service.downloadDir(request, response); } + /** + * 通过阿里云OSS链接下载文件 + * + * @param id + * @param attachment + * @return + */ @GetMapping("/downloadFile") + public ResponseEntity downloadFile( + @RequestParam("id") String id, + @RequestParam(value = "attachment", defaultValue = "false") Boolean attachment) { - public ResponseEntity downloadFile(@RequestParam("id") String id, @RequestParam(value = "attachment", defaultValue = "false") Boolean attachment) { - - - return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT).header("Location", this.service.downloadFile(id, attachment)).body(null); + return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT) + .header("Location", this.service.downloadFile(id, attachment)) + .body(null); } + /** + * 本地下载文件 + * + * @param id + * @param attachment + * @param response + */ + @GetMapping("/downloadFileLocal") + public void downloadFile( + @RequestParam("id") String id, + @RequestParam(value = "attachment", defaultValue = "false") Boolean attachment, + HttpServletResponse response) { + this.service.downloadFileLocal(id, attachment, response); + } } diff --git a/src/main/java/cn/lihongjie/coal/netDisk/dto/NetDiskTreeDto.java b/src/main/java/cn/lihongjie/coal/netDisk/dto/NetDiskTreeDto.java new file mode 100644 index 00000000..80ddd99a --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/netDisk/dto/NetDiskTreeDto.java @@ -0,0 +1,12 @@ +package cn.lihongjie.coal.netDisk.dto; + +import lombok.Data; + +import java.util.*; + +@Data +public class NetDiskTreeDto extends NetDiskDto { + + private List children = new ArrayList<>(); + +} diff --git a/src/main/java/cn/lihongjie/coal/netDisk/mapper/NetDiskMapper.java b/src/main/java/cn/lihongjie/coal/netDisk/mapper/NetDiskMapper.java index b9709b9e..c753fafa 100644 --- a/src/main/java/cn/lihongjie/coal/netDisk/mapper/NetDiskMapper.java +++ b/src/main/java/cn/lihongjie/coal/netDisk/mapper/NetDiskMapper.java @@ -31,4 +31,10 @@ public interface NetDiskMapper NetDiskEntity toEntity(CreateFileDto request); List toDto(List children); + + @Mappings({ + @Mapping(source = "children", target = "children", ignore = false, qualifiedByName = "copyChildrenTree" ) + }) + @Named("copyChildrenTree") + NetDiskTreeDto toTreeDto(NetDiskEntity root); } diff --git a/src/main/java/cn/lihongjie/coal/netDisk/repository/NetDiskRepository.java b/src/main/java/cn/lihongjie/coal/netDisk/repository/NetDiskRepository.java index 9eece739..f13f2802 100644 --- a/src/main/java/cn/lihongjie/coal/netDisk/repository/NetDiskRepository.java +++ b/src/main/java/cn/lihongjie/coal/netDisk/repository/NetDiskRepository.java @@ -3,6 +3,8 @@ package cn.lihongjie.coal.netDisk.repository; import cn.lihongjie.coal.base.dao.BaseRepository; import cn.lihongjie.coal.netDisk.entity.NetDiskEntity; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @@ -22,4 +24,47 @@ public interface NetDiskRepository extends BaseRepository { List findALlByParentNullAndOrganizationId(String organizationId); long countByParentNullAndOrganizationIdAndName(String organizationId, String name); + + @Query( + value = + """ + +with recursive + tmp as (select t.*, 0 as level + from t_net_disk t + where t.id = :id + union all + + select p.*, t.level + 1 as level + from t_net_disk p + inner join tmp t on p.id = t.parent_id), + + tmp2 as (select * from tmp order by level desc) + + +select string_agg(name, '/') +from tmp2 +""", + nativeQuery = true) + String idToPath(@Param("id") String id); + + @Query( + value = + """ + +with recursive tmp as (select regexp_split_to_array(:path, '/') as arr) + , tmp2 as (select tmp.arr, 1 as index, t.id as id, ARRAY_LENGTH(tmp.arr, 1) as len + from tmp + inner join t_net_disk t on t.parent_id is null and t.organization_id = :organizationId + union all + + select tx.arr as arr, tx.index + 1 as index, t.id as id, tx.len + from t_net_disk t + inner join tmp2 tx on t.parent_id = tx.id and t.name = tx.arr[tx.index + 1]) + +select id +from tmp2 where index = len +""", + nativeQuery = true) + String pathToId(@Param("path") String path, @Param("organizationId") String organizationId); } diff --git a/src/main/java/cn/lihongjie/coal/netDisk/service/NetDiskService.java b/src/main/java/cn/lihongjie/coal/netDisk/service/NetDiskService.java index dcea85e3..537491a5 100644 --- a/src/main/java/cn/lihongjie/coal/netDisk/service/NetDiskService.java +++ b/src/main/java/cn/lihongjie/coal/netDisk/service/NetDiskService.java @@ -33,6 +33,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Page; @@ -47,10 +48,7 @@ import java.io.InputStream; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; @Service @@ -65,6 +63,42 @@ public class NetDiskService extends BaseService allSlice) { if (allSlice.isEmpty() && request.getSliceIndex() != 0) { throw new BizException("第一个分片的index必须是0"); @@ -135,24 +169,7 @@ public class NetDiskService extends BaseService rootDir = - this.repository.findAll( - new Specification() { - @Override - public Predicate toPredicate( - Root root, - CriteriaQuery query, - CriteriaBuilder criteriaBuilder) { - - return criteriaBuilder.and( - criteriaBuilder.isNull(root.get("parent")), - criteriaBuilder.equal( - root.get("organizationId"), - Ctx.currentUser().getOrganizationId()), - criteriaBuilder.equal(root.get("entryType"), "0"), - criteriaBuilder.equal(root.get("name"), "/")); - } - }); + List rootDir = findRootDir(); if (rootDir.isEmpty()) { NetDiskEntity entity = new NetDiskEntity(); @@ -168,6 +185,27 @@ public class NetDiskService extends BaseService findRootDir() { + return this.repository.findAll( + new Specification() { + @Override + public Predicate toPredicate( + Root root, + CriteriaQuery query, + CriteriaBuilder criteriaBuilder) { + + return criteriaBuilder.and( + criteriaBuilder.isNull(root.get("parent")), + criteriaBuilder.equal( + root.get("organizationId"), + Ctx.currentUser().getOrganizationId()), + criteriaBuilder.equal(root.get("entryType"), "0"), + criteriaBuilder.equal(root.get("name"), "/")); + } + }); + } + public NetDiskDto createDir(CreateDirDto request) { validateDirName(request.getName()); @@ -741,4 +779,50 @@ public class NetDiskService extends BaseService