Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- programmers
- 백준
- 일정 관리
- 누적합
- 브루트포스
- 이분탐색
- BFS
- SpringBoot
- til
- 코딩테스트
- 계산기 만들기
- 이분 탐색
- Baekjoon
- 구현
- File
- ES
- Algorithm
- parametric search
- Elasticsearch
- 알고리즘
- Java
- 객체지향
- 완전탐색
- Generics
- 내일배움캠프
- binary search
- 해시
- querydsl
- Spring
- 프로그래머스
Archives
- Today
- Total
개발하는 햄팡이
[Springboot, Mysql, Mybatis] PasswordEncoder구현 - 검색 효율 높인 게시판 구현 본문
Back-End/Spring
[Springboot, Mysql, Mybatis] PasswordEncoder구현 - 검색 효율 높인 게시판 구현
hampangee 2024. 10. 23. 11:01SpringSecurity를 한 번 해봤다는 이유로 프로젝트에서 로그인 회원가입구현만 4번째...
매번 그냥 라이브러리에 있는 BCryptPasswordEncoder클래스를 가져와서 썼지만 이번에는 공부도 할겸해서 직접 PasswordEncoder를 구현했다.
코드 - PasswordEncoder Class
package com.hampang.boardserver.utils;
import java.security.MessageDigest;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SHA256Util {
public static final String ENCRYPTION_KEY = "SHA-256";
public static String encryptSHA256(String str) {
String SHA = null;
MessageDigest sh;
try {
// key값으로 인스턴스 만들기. MessageDigest : 메세지 해시값을 계산
sh = MessageDigest.getInstance(ENCRYPTION_KEY);
// str.getBytes() 문자열을 바이트 배열로 변환
// 해시 알고리즘은 바이트 단위로 데이터를 처리하므로, 문자열을 먼저 바이트로 변환
sh.update(str.getBytes());
// digest(): 바이트 배열로 변환된 데이터를 해시 값으로 변환
byte[] byteData = sh.digest();
// 바이트 배열을 헥사값으로 변환
StringBuffer sb = new StringBuffer();
for (byte byteDatum : byteData) {
sb.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1));
}
SHA = sb.toString();
} catch (Exception e) {
log.error("Encrypt Error - NoSuchAlgorithmException", e);
return null;
}
return SHA;
}
}
for문 부분이 암호화 부분인데 자세하게 살펴보면
- byteDatum & 0xff
- byte값을 0에서 255 사이의 양수로 변환
- byte값은 8비트로 표현되고 이는 -128 ~ 127까지의 값을 갖는데 & 0xff는 8비트 값을 유지하면서 음수값을 양수로 변환한다.
- 결과적으로 우리는 16진수 문자열로 받는게 목표이기 때문에 양수로 변환하는 것.
- (byteDatum & 0xff) + 0x100
- 한 자리 16진수 값 처리 : 0x100을 더하여 항상 두 자리 16진수로 표현될 수 있게한다.
- &연산 이후 값은 0~255 사이의 값을 갖게 되는데, 16진수로 변환했을때 값이 한자리일 경우가 있다.
- ex) 0x0A(10) -> "a"
- 우리는 암호를 항상 16자리 문자열을 만들기 위해 앞에 0을 붙여 "0a"가 나오도록 해야된다.
- 0x100을 더하면 266이 (256 + 10)이 연산되어 266이 되는데 이를 16진수로 변환하면 "10a"
- .substring(1)
- 마지막 두 자리를 잘라와서 항상 두 자리의 16진 수 값 얻기
- 바이트값이 두 자리일 경우에도 0x100을 더하기 때문에 최소 3자리가 나오게 된다. 이를 substring하여 최소 두자리 획득
- 일관된 두자리 해시값을 얻기 위함.
위 코드를 chatGPT를 돌려서 리팩토링 요청을 해봤는데
개선점:
- StringBuffer 대신 StringBuilder 사용:
- StringBuffer는 쓰레드 안전성을 보장하지만, 암호화 작업은 보통 단일 쓰레드에서 실행되므로 굳이 쓰레드 안전성을 고려할 필요가 없습니다.
- 대신 더 가벼운 **StringBuilder**를 사용하는 것이 성능 면에서 유리합니다.
- 예외 처리:
- NoSuchAlgorithmException 외에도 UnsupportedEncodingException 등이 발생할 수 있으므로, 적절하게 예외를 나눠서 처리하거나, 보다 구체적인 예외 처리가 필요할 수 있습니다.
- 암호화 메소드 이름:
- encryptSHA256이라는 메소드 이름은 기술적으로 정확하진 않습니다. SHA-256은 **암호화(encryption)**가 아니라 **해싱(hash)**입니다. 해싱은 원래 데이터로 다시 되돌릴 수 없는 일방향성 변환이기 때문에 hashSHA256 또는 generateSHA256Hash 같은 이름이 더 적합합니다.
- 바이트 배열을 16진수 문자열로 변환하는 더 간결한 방법:
- Integer.toString(...).substring(1) 방식도 동작하지만, String.format("%02x", byteDatum)을 사용하면 더 간단하고 읽기 쉬운 방식으로 2자리 16진수 문자열을 얻을 수 있습니다.
요렇게 답이 왔다...
확실히 리팩토링은 잘해주는 것 같다. 더 깔끔해보이는 느낌,,
리팩토링 된 코드
package com.hampang.boardserver.utils;
import java.security.MessageDigest;
import lombok.extern.log4j.Log4j2;
@Log4j2
public class SHA256Util {
public static final String ENCRYPTION_KEY = "SHA-256";
public static String hashSHA256(String str) {
String result = null;
try {
// SHA-256 인스턴스 생성
MessageDigest sh = MessageDigest.getInstance(ENCRYPTION_KEY);
// 문자열을 바이트 배열로 변환하고 해시 계산
byte[] byteData = sh.digest(str.getBytes());
// 바이트 배열을 16진수 문자열로 변환
StringBuilder sb = new StringBuilder();
for (byte byteDatum : byteData) {
sb.append(String.format("%02x", byteDatum)); // 2자리 16진수로 포맷팅
}
result = sb.toString();
} catch (Exception e) {
log.error("Hashing Error - NoSuchAlgorithmException", e);
return null;
}
return result;
}
}
근데 아무리 봐도 비트연산은 이해가 안된다....
플젝끝나고 비트연산 공부해야지
'Back-End > Spring' 카테고리의 다른 글
[Spring] 파일입출력 - 같은 파일 업로드 요청 시 계속 저장되는 문제 - Hash로 해결 (4) | 2025.07.29 |
---|---|
[Spring][JPA][뉴스피드 만들기] JPA의 변경 감지 (set메소드) vs save() 호출 - 무엇을 선택할까? (5) | 2025.06.14 |
[Spring][일정 관리 앱 만들기] CRUD 생성 - QueryDsl 쿼리문 코드 작성 (1) | 2025.05.24 |
[Spring][일정 관리 앱 만들기] CRUD 생성 - QueryDsl 시작하기 (2) | 2025.05.24 |
[ES, SpringBoot] 공지사항 게시판 검색 기능 구현하기 (2) : 데이터 저장 및 삭제 (0) | 2024.11.12 |