spring - Spring Security Web을 이용한 비밀번호 암호화(2022-10-24)
1. 서론
간만에 돌아온 spring이다.
우리가 여태까지 회원관리 프로젝트, 회원제 게시판, 각종 프로젝트를 하며 비밀번호를 참 많이 썼었다.
현재 개인적으로 웹을 만들기 때문에 상관없지만 만약 이걸 런칭한다고 생각하면
비밀번호는 100% 털린다고 봐야한다. 암만 봐도 해커들이 털기가 좋거든....
이번에 이 비밀번호를 암호화하는 것에 대해 알아보도록 하자.
2. 본론
먼저 의존성부터 주입시켜야 하는데 구글에 어디를 치라고 했더라????
maven repository을 검색해서 드가자~~~~~~~~~~~~
그러면 맨 위에 이런걸 볼 수 있다.
이걸 주입시키면 회원들이 비밀번호를 평문으로 입력했을 때, 알아서 랜덤값으로 암호화 시켜준다.
그래서 클릭해가지고 아무버전의 코드나 가져오도록 하자.
왜 아무버전을 가져오라고 했냐면??
${org.springframework-version}
이걸 붙여서 스프링 버전이랑 맞춰줄 생각이라 그랬다.
잘 붙여넣었다면 ctrl+F5눌러 저장해주도록 하고?
Maven Dependencies 쪽을 보면
잘 추가된걸 볼 수 있다.
이 녀석을 사용할 수 있는 코드는
BCryptPasswordEncoder
부터 시작한다.
2022.10.25 - [3층 - 개발의 장/ajax통신] - ajax - ajax를 이용해 아이디 중복체크 하는 예제 (2022-10-24)
ajax - ajax를 이용해 아이디 중복체크 하는 예제 (2022-10-24)
1. 서론 이번엔 예제2번을 활용하여 회원가입 시 아이디 중복체크를 해보도록하자. 2. 본론 수업 들으면서 get방식으로 중복체크를 했는데 필자는 post로 해보고 싶어서 고민했다. 근데? open객체를
sukw9512.tistory.com
필자는 위 예제에 회원가입 부분에 다음과 같이 작성했다.
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 32 33 34 | // 회원가입 검증 시작======================================================== public String register(MemberDTO dto, String confirmPw) { if (dto.getId() == null || dto.getId().isEmpty()) { return "필수 정보 입니다."; } if (dto.getPw() == null || dto.getPw().isEmpty()) { return "필수 정보 입니다."; } if (dto.getPw().equals(confirmPw) == false) { return "입력하신 두 비밀번호가 서로 다릅니다."; } MemberDTO check = dao.login(dto.getId()); if (check != null) { return "다른 아이디로 가입을 시도하세요."; } // 회원가입 암호화 시작======================================================= BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String cipherPassword = encoder.encode(dto.getPw()); System.out.println("암호화 적용 전 패스워드 : " + dto.getPw()); System.out.println("암호화된 패스워드 : " + cipherPassword); System.out.println("암호화된 패스워드 길이 : " + cipherPassword.length()); // 회원가입 암호화 도착======================================================= //모든 if문에서 걸러지지 않았다면 register에 입력값이 저장되며 dao.register(dto); //"가입 성공!" 이라는 문자열을 반환시켜준다. //이 반환값은 Controller에서 service.register라는 녀석이 소유하게 된다. return "가입 성공!"; } // 회원가입 검증 끝========================================================== | cs |
회원가입을 진행해서 console에 뭐가 찍히는지 확인해보자!
암호화가 진행되었다. 하지만 따로 setPw()을 하지 않아 일단 평문으로 패스워드가 들어갔다.
어찌되었든 패스워드 길이는 60byte인것을 알 수 있다.
그러면 의문이 생긴다. 평문의 길이가 길면 암호화된 패스워드도 길어지는거 아닌가?
그래서 이번엔 패스워드를 p@ssword로 넣어보려고 한다.
평문이 아무리 길어도 암호화된 패스워드 길이는 60으로 고정된다.
단방향 암호화 알고리즘이 동일하다면 길이는 동일하다.
그러면 암호화는 왜 필요한가?
해커에게서 시간을 벌기 위한 수단? 정도로 생각하면 될 거 같다.
출력해서 확인해봤지만 암호화에 전혀 규칙이 없기때문에 해커가 해킹하는 데에는 시간이 걸린다.
그 사이 클라이언트는 비밀번호를 바꾸게 되면 새로운 암호화로 갱신되기 때문에 해킹이 불가한 것.
....요 정도?
그러면 pw 칼럼의 용량을 60으로 바꾸고 회원가입을 하도록 하자.
60으로 바꾸고 항상 commit을 생활화 하자!
바뀐것도 확인했다!
회원가입 암호화 최종
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 32 33 34 35 | // 회원가입 검증 시작======================================================== public String register(MemberDTO dto, String confirmPw) { if (dto.getId() == null || dto.getId().isEmpty()) { return "필수 정보 입니다."; } if (dto.getPw() == null || dto.getPw().isEmpty()) { return "필수 정보 입니다."; } if (dto.getPw().equals(confirmPw) == false) { return "입력하신 두 비밀번호가 서로 다릅니다."; } MemberDTO check = dao.login(dto.getId()); if (check != null) { return "다른 아이디로 가입을 시도하세요."; } // 회원가입 암호화 시작======================================================= BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String cipherPassword = encoder.encode(dto.getPw()); System.out.println("암호화 적용 전 패스워드 : " + dto.getPw()); System.out.println("암호화된 패스워드 : " + cipherPassword); System.out.println("암호화된 패스워드 길이 : " + cipherPassword.length()); dto.setPw(cipherPassword); // 회원가입 암호화 도착======================================================= //모든 if문에서 걸러지지 않았다면 register에 입력값이 저장되며 dao.register(dto); //"가입 성공!" 이라는 문자열을 반환시켜준다. //이 반환값은 Controller에서 service.register라는 녀석이 소유하게 된다. return "가입 성공!"; } // 회원가입 검증 끝========================================================== | cs |
현재 회원 목록이다.
그러면 의문이 생긴다.
않이....그러면 로그인할 때 암호화된 비밀번호를 한땀한땀 쳐야된다고?
그건 좀 에바자너....
라고 할 수 있는데
그럴 경우를 대비에
matches가 있다.
클라이언트가 친 비밀번호(rawPassword)와 인코딩된 비밀번호와 검증한다.
로그인 암호화 검증 최종
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 32 | // 로그인 검증 시작========================================================== public String login(MemberDTO dto) { if (dto.getId().isEmpty()) { return "필수 정보 입니다."; } MemberDTO check = dao.login(dto.getId()); if (check == null) { return "아이디를 확인 후 다시 시도하세요."; } // 로그인 비밀번호 암호화 검증 시작=============================================== BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); if(encoder.matches(dto.getPw(), check.getPw()) == false) { return "비밀번호를 확인 후 다시 시도하세요."; } // 로그인 비밀번호 암호화 검증 도착=============================================== // if (check.getPw().equals(dto.getPw()) == false) { // return "비밀번호를 확인 후 다시 시도하세요."; // } //모든 if문에서 걸러지지 않았다면 아래와 같이 session값이 생성되며 session.setAttribute("id", check.getId()); session.setAttribute("pw", check.getPw()); session.setAttribute("name", check.getName()); session.setAttribute("email", check.getEmail()); //"로그인 성공" 이라는 문자열을 반환시켜준다. //이 반환값은 Controller에서 service.login이라는 녀석이 소유하게 된다. return "로그인 성공"; } // 로그인 검증 끝============================================================ | cs |
여기서 궁금한게 있다. 그러면 같은 비밀번호일 경우는 어떻게 검증하는가?
둘 다 평문은 1234인데 어떻게 알고 로그인 시키는가?
....모르지
그걸 알기 위해선 이 의존성을 개발한 개발자에게 가서 말해야 겠지...??;;;;
=====================================10월 25일 수정=======================================
동일한 비밀번호를 입력해도 암호문은 다르다.
salt(random값)을 평문과 더해서 암호화 알고리즘에 적용 후 암호화 과정을 거쳐 암호문이 생성된다.
즉, 같은 비밀번호라고 해도 둘의 알고리즘이 다르기 때문에 그 차이를 matches가 캐치하고 로그인을 적용시켜주는 것.
회원수정 암호화 검증 최종
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 32 33 | // 회원수정 검증 시작=========================================================== public String update(MemberDTO dto, String confirmPw) { //현재 로그인되어 있는 아이디의 세션값을 sessionId라는 변수에 저장한다. String sessionId = (String)session.getAttribute("id"); //변수가 비어있다면 로그인이 안되어 있는 것이기 때문에 아래와 같은 문자열을 반환 if(sessionId.isEmpty()) { return "로그인 후 이용해주세요."; } //input에 입력한 setter를 dto.getPw로 getter로 가져와 비밀번호확인과 같지 않다면 //아래와 같은 문자열을 반환 if(dto.getPw().equals(confirmPw) == false) { return "입력하신 비밀번호가 서로 다릅니다."; } // 회원수정 암호화 시작========================================================= BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String cipherPassword = encoder.encode(dto.getPw()); dto.setPw(cipherPassword); //모든 if문에서 걸러지지 않았다면 sessionID를 dto에 setter로 보내지며, 같이 딸려온 정보도 저장한다. dto.setId(sessionId); // 회원수정 암호화 도착========================================================= //dao가 dto가 가져온 아이디값을 조회하여 아이디값과 같이 딸려온 setter값들을 쿼리문에 맞게 //회원정보를 수정한다. dao.update(dto); //그 후 세션정보를 모두 삭제하고, //아래와 같은 문자열을 반환한다. session.invalidate(); return "수정 완료"; } // 회원수정 검증 끝============================================================= | cs |
회원 수정부터 해보자
수정할 회원님은 바로 이분!
비밀번호도 바꿨는데 잘 바뀌었다.
회원삭제 암호화 검증 최종
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 32 33 34 35 | // 회원삭제 검증 시작============================================================ public String delete(String pw, String confirmPw) { String sessionId = (String)session.getAttribute("id"); if(sessionId.isEmpty()) { return "로그인 후 이용해주세요."; } if(pw.isEmpty()) { return "필수 정보 입니다."; } if(pw.equals(confirmPw) == false) { return "입력하신 두 비밀번호를 확인하세요"; } MemberDTO result = dao.login(sessionId); // 회원삭제 암호화 시작=============================================================== BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); if(encoder.matches(pw, result.getPw()) == false) { return "비밀번호를 확인하세요."; } // 회원삭제 암호화 도착=============================================================== // if(result.getPw().equals(pw) == false) // return "비밀번호를 확인하세요."; //모든 if문에서 걸러지지 않았다면 dao가 sessionId와 동일한 아이디값을 //찾아 delete쿼리문을 실행한다. dao.delete(sessionId); //삭제하고 세션정보를 모두 지우고 session.invalidate(); //아래와 같은 문자열을 반환한다. return "삭제 완료"; } // 회원삭제 검증 끝============================================================== | cs |
회원삭제도 진행해보자.
test4를 없애보도록 하자.
삭제된걸 볼 수 있다.
3. 결론
참 이런걸 개발하신 분도 대단한 거 같다...
비밀번호를 찾을때도 matches를 이용하면 되려나??
곧 있으면 프로젝트인데 한번 시도해보는 것도 좋을 거 같다.
이번 프로젝트도 큰 마찰없이 스무스하게 잘 흘러갈 수 있기를....