
1. 암호화
1) 단방향 : 복구화가 안됨 / HASH
ex) SHA-512, BCrypt
2) 양방향
대칭키 : 키 교환이 어려워 상대방이 확인할 수 없음
디피-헬만(Diffie-Hellman) 알고리즘을 사용해서 서로만 알 수 있는 수학적 키를 교환
암호화해서 데이터 전송은 가능하나 신원 인증을 못함
비대칭키 : RSA
공형 암호
2. Junit으로 테스트하기
- 아무 sault나 막 붙일 수 없음
package shop.mtcoding.blog._core.util; import org.junit.jupiter.api.Test; import org.mindrot.jbcrypt.BCrypt; public class BCryptTest { @Test public void hashpw_test(){ String rawPassword = "1234"; String encPassword = BCrypt.hashpw(rawPassword,"_jooho"); System.out.println(encPassword); } }

- gensalt 사용하기
- 키 값이 매번 달라짐
package shop.mtcoding.blog._core.util; import org.junit.jupiter.api.Test; import org.mindrot.jbcrypt.BCrypt; public class BCryptTest { @Test public void gernsault_test() { String sault = BCrypt.gensalt(); System.out.println(sault); // 값이 매번 달라짐 } @Test public void hashpw_test(){ String rawPassword = "1234"; String encPassword = BCrypt.hashpw(rawPassword,"_jooho"); System.out.println(encPassword); } }




3. 더미 데이터에 password값을 암호화된 값으로 변경
insert into user_tb(username, password, email, created_at) values('ssar', '$2a$10$r86ew9RpjFF8kNfY1jiqRe', 'ssar@nate.com', now()); insert into user_tb(username, password, email, created_at) values('cos', '$2a$10$r86ew9RpjFF8kNfY1jiqRe', 'cos@nate.com', now()); insert into board_tb(title, content, user_id, created_at) values('제목1', '내용1', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목2', '내용2', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목3', '내용3', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목4', '내용4', 2, now()); insert into board_tb(title, content, user_id, created_at) values('title5', '내용5', 2, now()); insert into board_tb(title, content, user_id, created_at) values('title6', '내용6', 2, now()); insert into board_tb(title, content, user_id, created_at) values('title7', '내용7', 2, now()); insert into board_tb(title, content, user_id, created_at) values('title8', '내용8', 2, now()); insert into board_tb(title, content, user_id, created_at) values('title9', '내용9', 2, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글1', 1, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글2', 4, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글3', 4, 1, now()); insert into reply_tb(comment, board_id, user_id, created_at) values('댓글4', 4, 2, now());

4. UserRepository에 findById 만들기
- 매번 암호화되어 값이 달라지기 때문에 findByUsernameAndPassword을 사용할 수 없음
- user 객체에 id를 찾아서 순수한 password과 받은 password의 해쉬 값이랑 비교
package shop.mtcoding.blog.user; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog.board.BoardResponse; import java.util.List; @Repository // IoC에 new하는 방법 public class UserRepository { // DB에 접근할 수 있는 매니저 객체 // 스프링이 만들어서 IoC에 넣어둔다. // DI에서 꺼내 쓰기만 하면된다. private EntityManager em; // 컴포지션 // 생성자 주입 (DI 코드) public UserRepository(EntityManager em) { this.em = em; } @Transactional // db에 write 할때는 필수 public void save(UserRequest.JoinDTO requestDTO){ Query query = em.createNativeQuery("insert into user_tb(username, password, email, created_at) values(?,?,?, now())"); query.setParameter(1, requestDTO.getUsername()); query.setParameter(2, requestDTO.getPassword()); query.setParameter(3, requestDTO.getEmail()); query.executeUpdate(); } public User findByUsernameAndPassword(UserRequest.LoginDTO requestDTO) { Query query = em.createNativeQuery("select * from user_tb where username=? and password=?", User.class); // 알아서 매핑해줌 query.setParameter(1, requestDTO.getUsername()); query.setParameter(2, requestDTO.getPassword()); try { // 내부적으로 터지면 터지는 위치를 찾아서 내가 잡으면 됨 User user = (User) query.getSingleResult(); return user; } catch (Exception e) { throw new RuntimeException("아이디 혹은 비밀번호를 찾을 수 없습니다"); } } public User findByUsername(String username) { Query query = em.createNativeQuery("select * from user_tb where username=?", User.class); // 알아서 매핑해줌 query.setParameter(1, username); try { // 내부적으로 터지면 터지는 위치를 찾아서 내가 잡으면 됨 User user = (User) query.getSingleResult(); return user; } catch (Exception e) { throw new RuntimeException("아이디를 찾을 수 없습니다"); } } }
5. UserController에 /join수정하기
- user 객체에 id를 찾아서 순수한 password과 받은 password의 해쉬 값이랑 비교
package shop.mtcoding.blog.user; import jakarta.servlet.http.HttpSession; import lombok.AllArgsConstructor; import org.mindrot.jbcrypt.BCrypt; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import shop.mtcoding.blog._core.util.Script; @AllArgsConstructor @Controller // 파일을 리턴함 -> @ResponseBody로 메세지 자체를 리턴 public class UserController { // fianl 변수는 반드시 초기화 되어야 함 private final UserRepository userRepository; // null private final HttpSession session; // @AllArgsConstructor를 사용하면서 필요 없어짐 // public UserController(UserRepository userRepository, HttpSession session) { // this.userRepository = userRepository; // this.session = session; // } // 원래는 get요청이나 예외 post요청하면 됨 // 민감한 정보는 쿼리 스트링에 담아보낼 수 없음 //원래는 get요청이나 예외 post요청하면 됨 //민감한 정보는 쿼리 스트링에 담아보낼 수 없음 @PostMapping("/login") public String login(UserRequest.LoginDTO requestDTO) { String rawPassword= requestDTO.getPassword(); String encPassword= BCrypt.hashpw(rawPassword, BCrypt.gensalt());//레인보우 테이블에 안털림 requestDTO.setPassword(encPassword); // 1. 유효성 검사 if(requestDTO.getUsername().length() < 3) { throw new RuntimeException("username 길이가 너무 짧아요"); } // 2. 모델 필요 select * from user_tb where username=? and password=? User user = userRepository.findByUsername(requestDTO.getUsername()); // DB에 조회할때 필요하니까 데이터를 받음 // password 검증 if(BCrypt.checkpw(requestDTO.getPassword(), user.getPassword())){ // 순수한 password 넣기 throw new RuntimeException("패스워드가 틀렸습니다"); } session.setAttribute("sessionUser", user); return "redirect:/"; } @PostMapping("/join") public String join(UserRequest.JoinDTO requestDTO) { System.out.println(requestDTO); try{ userRepository.save(requestDTO); } catch (Exception e) { throw new RuntimeException("아이디가 중복되었어요"); // 위임시키면 로직의 변화없이 오류를 위임하고 끝남 } return "redirect:/loginForm"; } @GetMapping("/joinForm") // view만 원함 public String joinForm() { return "user/joinForm"; } @GetMapping("/loginForm") // view만 원함 public String loginForm() { return "user/loginForm"; } @GetMapping("/user/updateForm") public String updateForm() { return "user/updateForm"; } @GetMapping("/logout") public String logout() { // 1번 서랍에 있는 uset를 삭제해야 로그아웃이 됨 session.invalidate(); // 서랍의 내용 삭제 return "redirect:/"; } }


Share article