-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* ✨ feature/#12 : 5.4 식권 QR 코드 조회하기 response dto 정의 Signed-off-by: EunJiJung <[email protected]> * ✨ feature/#12: QR 코드 생성 util 구현 Signed-off-by: EunJiJung <[email protected]> * ♻️ refactor/#12 : QrUtil exception 처리 Signed-off-by: EunJiJung <[email protected]> * ♻️ refactor/#12 : qr response dto string -> byte[]로 변경 Signed-off-by: EunJiJung <[email protected]> * ✨ feature/#12: QR 코드 생성 서비스 구현 Signed-off-by: EunJiJung <[email protected]> * ♻️ refactor/#12 : qr 사이즈 153 x 153px로 변경 Signed-off-by: EunJiJung <[email protected]> * ✨ feature/#12 : qr code color, 로고 이미지 추가 Signed-off-by: EunJiJung <[email protected]> * ✨ feature/#12 : 5.4 식권 QR코드 조회하기 http 메서드 코드 추가 Signed-off-by: EunJiJung <[email protected]> * ♻️ refactor/#12 : QrUtil ticketId -> id로 네이밍 변경 Signed-off-by: EunJiJung <[email protected]> * ♻️ refactor/#12 : QrUtil tCommonException 정의 Signed-off-by: EunJiJung <[email protected]> * ✨ feature/#12 : aes 암호화 util 추가, qr id 암호화 추가 Signed-off-by: EunJiJung <[email protected]> --------- Signed-off-by: EunJiJung <[email protected]>
- Loading branch information
Showing
9 changed files
with
314 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.daon.onjung.core.utility; | ||
import com.daon.onjung.core.exception.error.ErrorCode; | ||
import com.daon.onjung.core.exception.type.CommonException; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.crypto.Cipher; | ||
import javax.crypto.spec.IvParameterSpec; | ||
import javax.crypto.spec.SecretKeySpec; | ||
import java.security.SecureRandom; | ||
import java.util.Base64; | ||
|
||
@Component | ||
public class AESUtil { | ||
|
||
@Value("${aes-util.algorithm}") | ||
private String algorithm; | ||
|
||
@Value("${aes-util.secret-key}") | ||
private String secret_key; | ||
|
||
private static final SecureRandom random = new SecureRandom(); | ||
|
||
// 랜덤 16바이트 IV 생성 | ||
public byte[] generateRandomIV() { | ||
byte[] iv = new byte[16]; // 16바이트 IV | ||
random.nextBytes(iv); | ||
return iv; | ||
} | ||
|
||
// 공통된 암호화/복호화 처리 | ||
private Cipher getCipher(int mode, byte[] keyBytes, byte[] iv) throws Exception { | ||
Cipher cipher = Cipher.getInstance(algorithm); | ||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); | ||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); | ||
cipher.init(mode, secretKeySpec, ivParameterSpec); | ||
return cipher; | ||
} | ||
|
||
// AES 암호화 | ||
public String encrypt_AES(String id) { | ||
try { | ||
byte[] keyBytes = Base64.getDecoder().decode(secret_key); | ||
byte[] iv = generateRandomIV(); | ||
|
||
Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, keyBytes, iv); | ||
byte[] encrypted = cipher.doFinal(id.getBytes()); | ||
|
||
// IV와 암호화된 데이터를 결합 (IV + Ciphertext) | ||
byte[] combined = new byte[iv.length + encrypted.length]; | ||
System.arraycopy(iv, 0, combined, 0, iv.length); | ||
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length); | ||
|
||
return Base64.getEncoder().encodeToString(combined); | ||
|
||
} catch (Exception e) { | ||
throw new CommonException(ErrorCode.AES_ENCRYPTION_ERROR); | ||
} | ||
} | ||
|
||
// AES 복호화 | ||
public String decrypt_AES(String encryptedText) { | ||
try { | ||
byte[] combined = Base64.getDecoder().decode(encryptedText); | ||
byte[] keyBytes = Base64.getDecoder().decode(secret_key); | ||
|
||
// IV와 암호화된 데이터 분리 | ||
byte[] iv = new byte[16]; | ||
byte[] encrypted = new byte[combined.length - 16]; | ||
System.arraycopy(combined, 0, iv, 0, 16); | ||
System.arraycopy(combined, 16, encrypted, 0, encrypted.length); | ||
|
||
Cipher cipher = getCipher(Cipher.DECRYPT_MODE, keyBytes, iv); | ||
byte[] decrypted = cipher.doFinal(encrypted); | ||
|
||
return new String(decrypted); | ||
|
||
} catch (Exception e) { | ||
throw new CommonException(ErrorCode.AES_DECRYPTION_ERROR); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package com.daon.onjung.core.utility; | ||
|
||
import com.daon.onjung.core.exception.error.ErrorCode; | ||
import com.daon.onjung.core.exception.type.CommonException; | ||
import com.google.zxing.WriterException; | ||
import com.google.zxing.client.j2se.MatrixToImageConfig; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import com.google.zxing.BarcodeFormat; | ||
import com.google.zxing.EncodeHintType; | ||
import com.google.zxing.client.j2se.MatrixToImageWriter; | ||
import com.google.zxing.common.BitMatrix; | ||
import com.google.zxing.qrcode.QRCodeWriter; | ||
|
||
import javax.imageio.ImageIO; | ||
import java.awt.*; | ||
import java.awt.image.BufferedImage; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
@Component | ||
public class QrUtil { | ||
|
||
// QR을 확인해야 하는 웹 서버 주소 | ||
@Value("${qr-web.url}") | ||
private String url; | ||
@Value("${qr-web.path}") | ||
private String path; | ||
|
||
@Value("${qr-web.logo_img_url}") | ||
private String logo_img_url; | ||
@Value("${qr-web.bg_img_url}") | ||
private String bg_img_url; | ||
|
||
@Value("${qr-web.qr-color-code}") | ||
private String qr_color_code; | ||
@Value("${qr-web.bg-color-code}") | ||
private String bg_color_code; | ||
|
||
/** | ||
* 주어진 링크를 인코딩하여 QR 코드 이미지를 생성하고, | ||
* 배경 이미지와 로고를 결합하여 byte 배열 형태로 반환하는 메서드 | ||
* | ||
* @param id | ||
* @return 배경 이미지와 합성된 QR 코드 이미지를 바이트 배열 형태로 반환 | ||
*/ | ||
public byte[] generateQrCodeImageByte(String id) { | ||
try { | ||
|
||
// QR 코드 데이터 생성 | ||
String baseUrl = url + path + id; | ||
|
||
// QR 코드 생성 옵션 설정 | ||
Map<EncodeHintType, Object> hintMap = new HashMap<>(); | ||
hintMap.put(EncodeHintType.MARGIN, 0); // 여백 없음 | ||
hintMap.put(EncodeHintType.CHARACTER_SET, "UTF-8"); | ||
|
||
// QR 코드 생성 | ||
QRCodeWriter qrCodeWriter = new QRCodeWriter(); | ||
int qrSize = 153; // QR 코드와 배경 이미지 크기를 동일하게 설정 | ||
BitMatrix bitMatrix = qrCodeWriter.encode(baseUrl, BarcodeFormat.QR_CODE, qrSize, qrSize, hintMap); | ||
|
||
// 색상 지정 | ||
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(Long.decode(qr_color_code).intValue(),Long.decode(bg_color_code).intValue()); | ||
|
||
BufferedImage qrCodeImage = MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig); | ||
|
||
// 배경 이미지 로드 (디자인 파일 경로) - 배경이 없으면 로고 이미지가 흰색으로 안 보이는 이슈 있음. | ||
BufferedImage originalBackgroundImage = ImageIO.read(new File(bg_img_url)); | ||
|
||
// 배경 이미지를 QR 코드 크기로 리사이즈 | ||
BufferedImage backgroundImage = new BufferedImage(qrSize, qrSize, BufferedImage.TYPE_INT_ARGB); | ||
Graphics2D bgGraphics = backgroundImage.createGraphics(); | ||
bgGraphics.drawImage(originalBackgroundImage, 0, 0, qrSize, qrSize, null); | ||
bgGraphics.dispose(); | ||
|
||
// 로고 이미지 로드 | ||
BufferedImage logoImage = ImageIO.read(new File(logo_img_url)); | ||
|
||
// 배경 이미지 위에 QR 코드 합성 | ||
BufferedImage combinedImage = new BufferedImage(qrSize, qrSize, BufferedImage.TYPE_INT_ARGB); | ||
Graphics2D g = combinedImage.createGraphics(); | ||
|
||
// 배경 이미지 그리기 | ||
g.drawImage(backgroundImage, 0, 0, null); | ||
|
||
// QR 코드 중앙에 그리기 | ||
int qrX = (qrSize - qrCodeImage.getWidth()) / 2; | ||
int qrY = (qrSize - qrCodeImage.getHeight()) / 2; | ||
g.drawImage(qrCodeImage, qrX, qrY, null); | ||
|
||
// QR 코드 중앙에 로고 삽입 | ||
int logoWidth = qrCodeImage.getWidth() / 4; // QR 코드 크기의 1/4 크기로 로고 설정 | ||
int logoHeight = qrCodeImage.getHeight() / 4; | ||
int logoX = qrX + (qrCodeImage.getWidth() - logoWidth) / 2; // QR 코드 중앙에 배치 | ||
int logoY = qrY + (qrCodeImage.getHeight() - logoHeight) / 2; | ||
g.drawImage(logoImage, logoX, logoY, logoWidth, logoHeight, null); | ||
|
||
// 리소스 해제 | ||
g.dispose(); | ||
|
||
// 결과 이미지를 바이트 배열로 변환 | ||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
ImageIO.write(combinedImage, "png", byteArrayOutputStream); | ||
byteArrayOutputStream.flush(); | ||
byte[] combinedImageBytes = byteArrayOutputStream.toByteArray(); | ||
byteArrayOutputStream.close(); | ||
|
||
return combinedImageBytes; | ||
|
||
} catch (WriterException e) { | ||
throw new CommonException(ErrorCode.QR_CODE_GENERATION_ERROR); | ||
} catch (IOException e) { | ||
throw new CommonException(ErrorCode.QR_CODE_IMAGE_PROCESSING_ERROR); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
src/main/java/com/daon/onjung/event/application/dto/response/ReadTicketBriefResponseDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.daon.onjung.event.application.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class ReadTicketBriefResponseDto { | ||
|
||
@NotNull(message = "qr_base64는 null일 수 없습니다.") | ||
@JsonProperty("qr_base64") | ||
private final byte[] qrBase64; | ||
|
||
@Builder | ||
public ReadTicketBriefResponseDto( | ||
byte[] qrBase64 | ||
) { | ||
this.qrBase64 = qrBase64; | ||
} | ||
|
||
public static ReadTicketBriefResponseDto fromEntity( | ||
byte[] qrBase64 | ||
) { | ||
|
||
return ReadTicketBriefResponseDto.builder() | ||
.qrBase64(qrBase64) | ||
.build(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/daon/onjung/event/application/service/ReadTicketBriefService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.daon.onjung.event.application.service; | ||
|
||
import com.daon.onjung.core.utility.AESUtil; | ||
import com.daon.onjung.core.utility.QrUtil; | ||
import com.daon.onjung.event.application.dto.response.ReadTicketBriefResponseDto; | ||
import com.daon.onjung.event.application.usecase.ReadTicketBriefUseCase; | ||
import com.daon.onjung.event.repository.mysql.TicketRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ReadTicketBriefService implements ReadTicketBriefUseCase { | ||
|
||
private final TicketRepository ticketRepository; | ||
|
||
private final QrUtil qrUtil; | ||
private final AESUtil aesUtil; | ||
|
||
@Override | ||
@Transactional(readOnly = true) | ||
public ReadTicketBriefResponseDto execute(Long id) { | ||
|
||
// 암호화 | ||
String encryptedId = aesUtil.encrypt_AES(id.toString()); | ||
|
||
byte[] qrCodeBytes = qrUtil.generateQrCodeImageByte(encryptedId); | ||
|
||
return ReadTicketBriefResponseDto.fromEntity(qrCodeBytes); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/com/daon/onjung/event/application/usecase/ReadTicketBriefUseCase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.daon.onjung.event.application.usecase; | ||
|
||
import com.daon.onjung.core.annotation.bean.UseCase; | ||
import com.daon.onjung.event.application.dto.response.ReadTicketBriefResponseDto; | ||
|
||
@UseCase | ||
public interface ReadTicketBriefUseCase { | ||
|
||
/** | ||
* 식권 QR 코드 조회하기 | ||
* @param id 티켓 ID | ||
* @return 식권 QR 코드 조회 응답 DTO | ||
*/ | ||
ReadTicketBriefResponseDto execute(Long id); | ||
} |