1. 단순한 엑셀 다운로드 기능
2. 암호화 방법
728x90
반응형

엑셀 파일을 만들고 업로드/다운로드 기능을 만들었다고 가정하자. 다운로드 된 엑셀에 보안 강화를 위해 암호화를 하고 싶을 수 있다. 암호화하는 방법을 간단하게 공유해본다. 환경은 아래와 같다.
- 엑셀 모듈
- 엑셀 기능이 커스터마이징된 모듈을 별도로 개발하는 것을 가정함
- Java + Gradle
- org.apache.poi (자세한 사항은 아래 dependencies 참고
// build.gradle dependencies { implementation 'org.jsoup:jsoup:1.15.3' implementation group: "org.apache.poi", name: "poi", version: "4.1.2" implementation group: "org.apache.poi", name: "poi-ooxml", version: "4.1.2" }
kotlin
- 애플리케이션
- 스프링부트(3.0.5) + 코틀린(1.7.22)
- 단순히 위 엑셀 모듈을 dependencies하여 사용하는 곳이다
1. 단순한 엑셀 다운로드 기능
먼저 암호화 기능을 추가하기 전의 모습을 소개해본다.
public interface ExcelSupport {}
kotlin
public class ExcelWriter { private static final int DEFAULT_START_ROW = 0; public <T extends ExcelSupport> String write(List<T> sources) { return this.write(DEFAULT_START_ROW, sources); } public <T extends ExcelSupport> String write(int startRow, List<T> sources) { if (sources.isEmpty()) throw new ExcelWriteException("sources must not be empty"); try (SXSSFWorkbook wb = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE)) { wb.setCompressTempFiles(true); SXSSFSheet sheet = wb.createSheet(sheetName); CellStyle headerCellStyle = config.getHeaderStyle().defineCellStyle(wb); initCellDateFormat(wb); createHeader(startRow, headerCellStyle, sources.get(0).getClass(), sheet); createRow(startRow, sources, sheet); String path = getTempDir(); try (FileOutputStream out = new FileOutputStream(path)) { wb.write(out); out.close(); wb.dispose(); return path; } } catch (IOException | IllegalAccessException ex) { throw new ExcelWriteException(ex); } } }
kotlin
위 코드를 보면 내부함수들도 보인다. 여기서 다룰 핵심 코드는 아니기에 아래에 접은글로 공유한다.
더보기
private void initCellDateFormat(SXSSFWorkbook wb) { CellStyle dateCellStyle = wb.createCellStyle(); dateCellStyle.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(DateTypeNormalizer.DATE_FORMAT)); CellStyle dateTimeCellStyle = wb.createCellStyle(); dateTimeCellStyle.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(DateTypeNormalizer.DATETIME_FORMAT)); this.dateFormatStyles = Map.of("DATE", dateCellStyle, "DATETIME", dateTimeCellStyle); } private <T extends ExcelSupport> void createHeader(int startRow, CellStyle headerCellStyle, Class<T> clazz, SXSSFSheet sheet) { Field[] fields = clazz.getDeclaredFields(); SXSSFRow row = sheet.createRow(startRow); row.setHeight(config.getHeaderRowHeight()); for (Field field : fields) { if (!field.isAnnotationPresent(ExcelExport.class)) continue; ExcelExport ann = field.getAnnotation(ExcelExport.class); SXSSFCell cell = row.createCell(ann.order()); cell.setCellValue(ann.name()); cell.setCellStyle(headerCellStyle); } } private void createRow(int startRow, List<? extends ExcelSupport> sources, SXSSFSheet sheet) throws IllegalAccessException { int inputStartRow = startRow + 1; for (ExcelSupport source : sources) { Field[] fields = source.getClass().getDeclaredFields(); SXSSFRow row = sheet.createRow(inputStartRow++); setCellValue(source, fields, row); } } private static String getTempDir() { String fileName = UUID.randomUUID().toString(); return DEFAULT_TEMP_DIR + "/" + fileName + ".xlsx"; }
kotlin
실제 애플리케이션에서는 아래와 같이 사용하고 있다. 넥서스 레포지토리를 사용하여 모듈을 등록하고 이 모듈을 원하는 애플리케이션에서 사용하고 있다.
// build.gradle dependencies { // Excel Module. 존재하지 않는 라이브러리이다. 어디까지나 예시임 implementation("devvkkid", "excel-module", "1.2.0") }
kotlin
private fun convertToExcelResponse(result: List<UserResponse.Excel>, password: String): ExcelResponse { val writer = ExcelWriter("샘플정보") try { val createdFilePath = writer.write(result, password) val uploadFileName = fileManager.upload(createdFilePath) val downloadFileName = "샘플정보-${LocalDateTimeFormatter.parseToTrimPattern(LocalDateTime.now())}" return ExcelResponse( fileManager.download(uploadFileName, downloadFileName), result.size ) } catch (ex: ExcelWriteException) { throw ExcelRuntimeException(ExcelRuntimeErrorCode.WRITE_ERROR, ex) } }
kotlin
fileManager 정보는 여기서 핵심은 아니라 아래 접은글로 공유한다.
더보기
interface FileManager { fun upload(filePath: String): String fun upload(prefixPath: String, filePath: String): String fun download(fileName: String, downloadFileName: String): String fun getPresignedUrl(key: String, contentType: String, signatureDuration: Duration): String } @Component class S3ExcelFileManagerClient( private val s3Client: S3Client, private val s3Presigner: S3Presigner, ) : FileManager { override fun upload(filePath: String): String { return this.putObject(filePath) } /** * S3 Bucket으로 Object를 upload 합니다 * * @param filePath originalFile Path * @return 업로드한 fileName */ fun putObject(filePath: String, prefixPath: String? = null): String { val objectKey = if (prefixPath.isNullOrBlank()) "$KEY_PREFIX/${LocalDate.now().format(dateFormatter)}/${Path(filePath).fileName}" else "$KEY_PREFIX/${LocalDate.now().format(dateFormatter)}/$prefixPath/${Path(filePath).fileName}" val por = PutObjectRequest.builder() .bucket(bucketName) .key(objectKey) .build() try { s3Client.putObject(por, RequestBody.fromBytes(getObjectFile(filePath))) return objectKey } catch (ex: RuntimeException) { throw ExcelFileManagerException(FAILED_FILE_UPLOAD, ex) } finally { Path(filePath).deleteExisting() } } }
kotlin
2. 암호화 방법
이제 암호화하는 방법을 공유해본다. 위에 ExcelWriter 클래스의 write 함수를 변경할 것이다. try/catch 구문이 있었는데 여기 내부에 아래와 같은 코드를 넣으면 된다.
try (SXSSFWorkbook wb = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE)) { // 생략 try (FileOutputStream out = new FileOutputStream(path)) { // 암호화 설정 POIFSFileSystem fs = new POIFSFileSystem(); EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); Encryptor enc = info.getEncryptor(); enc.confirmPassword(password); // 작성한 데이터를 암호화된 스트림에 저장 try (OutputStream encOut = enc.getDataStream(fs)) { wb.write(encOut); } // 파일 저장 fs.writeFilesystem(out); out.close(); wb.dispose(); return path; } } catch (IOException | IllegalAccessException | GeneralSecurityException ex) { throw new ExcelWriteException(ex); }
kotlin
- POIFS : 마이크로소프트 오피스의 OLE 2 Compound document 파일 포맷을 읽고 쓰는 컴포넌트. 모든 오피스 파일 포맷은 OLE2 방식이므로 하위 모든 컴포넌트의 기반이 된다. (출처: 위키백과)
- EncryptionInfo : 사용할 암호 및 해싱 알고리즘을 지정하는 추가 매개변수를 제공
- Encryptor : 실제 암호화를 위해 암호나 Key를 입력받는 클래스
- 작성한 데이터를 암호화된 스트림에 저장하고, 파일에 바이트 스트림을 저장하는 과정을 잊으면 안된다.
자료 출처
- Apache POI - Encryption support
- 자바 POI 엑셀 다운로드 암호화
.
728x90
반응형
'Spring' 카테고리의 다른 글
application.yml에서 AWS 환경변수를 지워도 동작하는 이유 (0) | 2023.08.24 |
---|---|
내가 만든 라이브러리/모듈을 로컬에서 사용하기 (0) | 2023.08.07 |
AWS SQS와 EventBridge를 활용한 스케줄러 만들기 (0) | 2023.06.27 |
Mockito에서 only과 time(1)은 어떤 차이일까? (0) | 2023.05.07 |
스프링 JPA 환경 변수 중 몰랐던 것들 (0) | 2022.11.01 |
Comment