Skip to content

Commit

Permalink
#26 Feat: 메타데이터를 통한 여권 유효성 검사 & 재학증StudentCertification 인증시간 추가 [박한솔]
Browse files Browse the repository at this point in the history
  • Loading branch information
pjhcsols committed Oct 20, 2024
1 parent cb9d1de commit 88bdfb4
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 10 deletions.
2 changes: 1 addition & 1 deletion web3-credential-server/build/resources/main/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ VALUES
('[email protected]', '$2a$10$EXAMPLEHASHFORUSERPASSWORD'),
('3751271433', '$2a$10$skVt4kLn.95UGnrasmxQku5AGhhg6fESNtg/Ndw3iBQin.SqHrIdK');


INSERT INTO wallets (user_id, pdfUrl,privateKey, publicKey)
VALUES
(1,'https://basilium-product-bucket.s3.ap-northeast-2.amazonaws.com/1_certifications.pdf','privateKeyForUser1', 'publicKeyForUser1'),
(2,null,'privateKeyForUser2', 'publicKeyForUser2'),
(3,null,'privateKeyForUser2', 'publicKeyForUser2');



Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import web3.service.wallet.WalletService;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -45,8 +46,8 @@ public ResponseEntity<String> registerCertification(
@RequestParam("univName") String univName,
@RequestParam("univCheck") Boolean univCheck) {

StudentCertificationDto certificationDto = new StudentCertificationDto(email, univName, univCheck);

LocalDateTime certifiedDate = LocalDateTime.now(); // 현재 시간
StudentCertificationDto certificationDto = new StudentCertificationDto(email, univName, univCheck, certifiedDate);
identityService.registerStudentCertification(walletId, certificationDto, file);

return ResponseEntity.ok("재학증이 성공적으로 등록되었습니다.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package web3.controller.cert;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Mono;
import web3.service.cert.PassportService;
import web3.service.dto.cert.PassportRequestDto;

import java.io.IOException;
import java.util.Base64;

@RestController
@RequestMapping("/api/passport")
public class PassportController {

private final PassportService passportService;

public PassportController(PassportService passportService) {
this.passportService = passportService;
}

@Operation(summary = "여권 유효성 검사", description = "여권의 유효성을 검사합니다.")
@PostMapping("/check-validity")
public Mono<ResponseEntity<String>> checkPassportValidity(
@Parameter(description = "여권 요청 정보", required = true)
@RequestParam("certFile") MultipartFile certFile,
@RequestParam("keyFile") MultipartFile keyFile,
@RequestParam("certPassword") String certPassword,
@RequestParam("userName") String userName,
@RequestParam("identity") String identity,
@RequestParam("passportNo") String passportNo,
@RequestParam("issueDate") String issueDate,
@RequestParam("expirationDate") String expirationDate,
@RequestParam("birthDate") String birthDate) {

String certFileEncoded = encodeFileToBase64(certFile);
String keyFileEncoded = encodeFileToBase64(keyFile);

PassportRequestDto passportRequestDto = new PassportRequestDto(certFileEncoded, keyFileEncoded, certPassword, userName, identity, passportNo, issueDate, expirationDate, birthDate);

return passportService.checkPassportValidity(passportRequestDto)
.map(response -> ResponseEntity.ok("여권 유효성 검사 성공: " + response))
.onErrorReturn(ResponseEntity.badRequest().body("여권 유효성 검사에 실패했습니다."));
}

private String encodeFileToBase64(MultipartFile file) {
try {
byte[] fileContent = file.getBytes();
return Base64.getEncoder().encodeToString(fileContent);
} catch (IOException e) {
throw new RuntimeException("파일 인코딩 실패", e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ public void registerStudentCertification(Long walletId, StudentCertificationDto
// 메타데이터 키 및 문자열 생성
// 등록 및 인증시간 포함하기
String metadataKey = "재학증_" + walletId;
String metadataString = String.format("{\"email\":\"%s\",\"univName\":\"%s\",\"univ_check\":%b}",
String metadataString = String.format("{\"email\":\"%s\",\"univName\":\"%s\",\"univ_check\":%b,\"certified_date\":\"%s\"}",
certificationDto.getEmail(),
certificationDto.getUnivName(),
certificationDto.isUnivCheck());
certificationDto.isUnivCheck(),
certificationDto.getCertifiedDate());

String fileName = generatePdfFileName(walletId);
byte[] result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package web3.service.cert;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import web3.service.dto.cert.PassportRequestDto;

import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class PassportService {

private final WebClient webClient;
private final WebClient webClientToken; // 액세스 토큰 요청을 위한 WebClient
private static final String BASE_URL = "https://development.codef.io";
private static final String API_URL = "/v1/kr/public/mw/passport-data/status";
private static final String BASE_TOKEN_URL = "https://oauth.codef.io";
private static final String ACCESS_TOKEN_URL = "/oauth/token";
private static final String CLIENT_ID = "86640213-3b83-461a-97ab-2491d68a2052";

public PassportService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl(BASE_URL).build();
this.webClientToken = webClientBuilder.baseUrl(BASE_TOKEN_URL).build(); // 수정된 부분
}

// 액세스 토큰을 가져오는 메서드
private Mono<String> fetchAccessToken() {
return webClientToken.post()
.uri(ACCESS_TOKEN_URL) // 추가된 경로
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((CLIENT_ID + ":" + CLIENT_SECRET).getBytes()))
.header("Content-Type", "application/x-www-form-urlencoded")
.bodyValue("grant_type=client_credentials&scope=read")
.retrieve()
.bodyToMono(Map.class)
.map(response -> (String) response.get("access_token"))
.doOnError(e -> log.error("Error fetching access token: {}", e.getMessage()));
}

// RSA 암호화 메서드
private String encryptRSAPassword(String password) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(PUBLIC_KEY_STR);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(password.getBytes(StandardCharsets.UTF_8)));
}

// 패스포트 유효성 검사 메서드
public Mono<Map<String, Object>> checkPassportValidity(PassportRequestDto passportRequestDto) {
return fetchAccessToken()
.flatMap(accessToken -> {
try {
Map<String, String> requestBody = createRequestBody(passportRequestDto);
log.info("requestBody: {}", requestBody);
return webClient.post()
.uri(API_URL)
.header("Authorization", "Bearer " + accessToken)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.bodyValue(requestBody)
.retrieve()
.bodyToMono(String.class)
.flatMap(responseBody -> {
log.info("Response: {}", responseBody);
ObjectMapper objectMapper = new ObjectMapper();
try {
String decodedResponseBody = URLDecoder.decode(responseBody, StandardCharsets.UTF_8.name());
Map<String, Object> jsonResponse = objectMapper.readValue(decodedResponseBody, new TypeReference<Map<String, Object>>() {});
return Mono.just(jsonResponse);
} catch (JsonProcessingException e) {
log.error("Error processing JSON: {}", e.getMessage());
return Mono.error(e); // 예외를 반환
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
})
.doOnError(e -> log.error("Error checking passport validity: {}", e.getMessage()));
} catch (Exception e) {
return Mono.error(e);
}
});
}

private Map<String, String> createRequestBody(PassportRequestDto passportRequestDto) throws Exception {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("organization", "0002");
requestBody.put("loginType", "2");
requestBody.put("certType", "1");
requestBody.put("certFile", passportRequestDto.getCertFileEncoded());
requestBody.put("keyFile", passportRequestDto.getKeyFileEncoded());
requestBody.put("certPassword", encryptRSAPassword(passportRequestDto.getCertPassword()));
requestBody.put("userName", passportRequestDto.getUserName());
requestBody.put("userName1", passportRequestDto.getUserName());
requestBody.put("identity", passportRequestDto.getIdentity());
requestBody.put("passportNo", passportRequestDto.getPassportNo());
requestBody.put("issueDate", passportRequestDto.getIssueDate());
requestBody.put("expirationDate", passportRequestDto.getExpirationDate());
requestBody.put("birthDate", passportRequestDto.getBirthDate());
return requestBody;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package web3.service.dto.Identity;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class StudentCertificationDto {
private String email;
private String univName;
private boolean univCheck;
private LocalDateTime certifiedDate; // 새로운 필드 추가

public StudentCertificationDto(String email, String univName, boolean univCheck) {

public StudentCertificationDto(String email, String univName, boolean univCheck, LocalDateTime certifiedDate) {
this.email = email;
this.univName = univName;
this.univCheck = univCheck;
this.certifiedDate = certifiedDate; // 새로운 필드 초기화
}

public String getEmail() {
Expand All @@ -24,4 +31,8 @@ public boolean isUnivCheck() {
return univCheck;
}

public String getCertifiedDate() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
return certifiedDate.format(formatter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package web3.service.dto.cert;

public class PassportRequestDto {
private String certFileEncoded; // 인증서 파일 Base64 인코딩
private String keyFileEncoded; // 키 파일 Base64 인코딩
private String certPassword; // 인증서 비밀번호
private String userName; // 사용자 이름
private String identity; // 주민등록번호
private String passportNo; // 여권 번호
private String issueDate; // 발급일
private String expirationDate; // 만료일
private String birthDate; // 생년월일

public PassportRequestDto(String certFileEncoded, String keyFileEncoded, String certPassword,
String userName, String identity, String passportNo,
String issueDate, String expirationDate, String birthDate) {
this.certFileEncoded = certFileEncoded;
this.keyFileEncoded = keyFileEncoded;
this.certPassword = certPassword;
this.userName = userName;
this.identity = identity;
this.passportNo = passportNo;
this.issueDate = issueDate;
this.expirationDate = expirationDate;
this.birthDate = birthDate;
}

// Getters
public String getCertFileEncoded() {
return certFileEncoded;
}

public String getKeyFileEncoded() {
return keyFileEncoded;
}

public String getCertPassword() {
return certPassword;
}

public String getUserName() {
return userName;
}

public String getIdentity() {
return identity;
}

public String getPassportNo() {
return passportNo;
}

public String getIssueDate() {
return issueDate;
}

public String getExpirationDate() {
return expirationDate;
}

public String getBirthDate() {
return birthDate;
}
}
6 changes: 2 additions & 4 deletions web3-credential-server/src/main/resources/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ VALUES
('[email protected]', '$2a$10$EXAMPLEHASHFORUSERPASSWORD'),
('3751271433', '$2a$10$skVt4kLn.95UGnrasmxQku5AGhhg6fESNtg/Ndw3iBQin.SqHrIdK');


INSERT INTO wallets (user_id, pdfUrl,privateKey, publicKey)
VALUES
(1,'https://basilium-product-bucket.s3.ap-northeast-2.amazonaws.com/1_certifications.pdf','privateKeyForUser1', 'publicKeyForUser1'),
(2,null,'privateKeyForUser2', 'publicKeyForUser2'),
(3,null,'privateKeyForUser2', 'publicKeyForUser2');



(3,null,'privateKeyForUser2', 'publicKeyForUser2');

0 comments on commit 88bdfb4

Please sign in to comment.