Search
📤

비밀번호를 잊은 유저들을 위해 비밀번호 변경 메일 발송하기

Person
toybo427@gmail.com
임준형
김시은
김시은
태그
목차

비밀번호를 잊어버린 유저는 어떻게 해야 하나요?

일반적으로 메일로 임시 비밀번호를 발급해줍니다.
하지만 임시 비밀번호를 발급받게 된다면, 저희팀 프로젝트 비밀번호 변경 프로세스상 로그인을 하고 프로필에 들어가서 임시비밀번호를 입력하고 새 비밀번호로 교체 해야합니다. 그러면 유저입장에서는 너무 귀찮겠죠.
그래서 비밀번호를 변경할 수 있는 url 을 이메일로 전송하고 그 url 을 클릭하면 비밀번호를 바꿀 수 있는 창을 띄우도록 서비스 로직을 세웠습니다.

비밀번호 변경 서비스 로직

처음에는 비밀번호 수정을 원할 경우, 이메일을 입력시 해당 이메일에 임시 비밀번호를 발송하고, 이를 DB에서 수정하는 것으로 생각했습니다. 하지만 저희 웹 특성상 개인정보를 받는 것이 이메일 밖에 없기 때문에 누군가 악의적으로 이메일을 입력 후 비밀번호 변경 시도할 경우 비밀번호가 게속 바꿀 수 있다는 생각이 들었습니다.
따라서 이메일을 입력하면 비밀번호를 변경할 수 있는 url을 이메일로 보내는 것으로 결정했습니다.
1.
비밀번호 변경 url을 입력한 메일로 전송합니다.
2.
입력한 메일은 sns 로그인이 아닌 저희 웹에서 회원가입한 계정이므로 이메일과 provider는 local인 것으로 DB에서 찾습니다.
3.
없다면 존재하지 않는 것으로 예외를 터뜨릴지 고민..
4.
입력한 이메일로 (내일 고찰과 함께 추가적으로 작성하겠슴돠..)
@임준형 여기에 대략적으로 로직 한번만 써주심 감사하겠습니더

문제의 발생

준형 님이 아래와 같이 유저에게 임시토큰을 담은 url 을 메일로 보내는 데에 성공했습니다.
하지만 이 링크를 postman 으로 responseBody 에 새로운 비밀번호를 담아 보내면 아무런 반응이 나타나지 않는 것이였습니다.

문제의 해결

1. SSL 인증서 문제

처음에는 https:// 로 시작하도록 적었기 때문에 ssl 인증을 할 수 없다는 오류가 발생하였습니다. 따라서 https:// → http:// 로 변경해주었습니다. 하지만 이는 서버에 배포할 때 알맞은 엔드포인트로 변경해주어야 합니다.

2. 데이터 바인딩 문제

또한 @RequestParam 로 이미 임시토큰을 받기 때문에 dto 에서 해당 필드를 제거해야 합니다.

3. 컨트롤러 생성

메일로 받은 url 을 클릭하면 비밀번호를 변경할 수 있는 주소를 반환하도록 컨트롤러를 생성하였습니다.
@GetMapping("/callback") public String getChangePasswordUrl(@RequestParam String tempToken) { return "http://localhost:5173/password/change?tempToken=" + tempToken; }
Java
복사
이를 통해 받은 url 을 이용하여 새로운 비밀번호를 설정하는 컨트롤러를 생성하였습니다.
@PostMapping("/change") public void getUriMailToken(@RequestParam String tempToken, @RequestBody ChangePasswordDto changePasswordDto) { forgotPasswordService.changePassword(changePasswordDto, tempToken); }
Java
복사

4. 권한 문제

에러로그에서 계속 권한문제가 나타났습니다. 따라서 아래와 같이 시큐리티설정과 웹토큰 설정을 변경해주었습니다.
// JwtAuthenticationFilter requestURI.startsWith("/password") // SecurityConfig .requestMatchers("/password/**").permitAll()
Java
복사

5. 토큰에서 이메일 받아오기

tokenUtil.getEmailFromToken 메서드를 사용하여 유저의 이메일을 받아오고, 이를 이용하여 유저의 비밀번호를 변경해야 합니다. 하지만 존재하는 유저임에도 불구하고 계속 존재하지 않는 유저라는 에러가 발생했습니다.

tokenUtil

public String getEmailFromToken(String token) { String email = Jwts.parser().setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody().getSubject(); return email; }
Java
복사
문제가 발생한 메소드는 이부분이였습니다. 곰곰히 생각해보니 소셜로그인이 들어오면서 토큰 생성시 userId 로 토큰을 만들고 있었다는 것을 간과하였습니다. 따라서 아래와 같이 메서드를 변경하여 주었습니다.
public String getEmailFromToken(String token) { String userId = Jwts.parser().setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody().getSubject(); String email = memberRepository.findById(Long.parseLong(userId)).get().getEmail(); log.info("-------------------------유저 이메일: " + email); return email; }
Java
복사
이메일인줄 알았던 값은 userId 였기 때문에 계속 오류가 발생하는 것이였습니다. 따라서 이메일을 찾는 로직을 더 추가해주었습니다.

6. 비밀번호 변경하려는 멤버 찾기

현재 소셜 로그인과 로컬로그인에서 이메일이 겹치는 경우가 생길 수 있습니다. 때문에 provider 를 이용해서 유일한 멤버를 도출해야 합니다. 따라서 아래와 같이 코드를 수정하였습니다.

ForgotPasswordService

// 이메일을 사용하여 멤버 찾기 Member memberToUpdate = memberRepository.findByEmail(email) .orElseThrow(() -> new ApiException(ErrorType.USER_NOT_FOUND)); // 이메일을 사용하여 멤버 찾기 Member memberToUpdate = memberRepository.findByEmailAndProvider(email, "local") .orElseThrow(() -> new ApiException(ErrorType.USER_NOT_FOUND));
Java
복사

테스트

1. 리퀘스트 보내기

2. 메일 확인하기

3. 해당 링크로 적절한 값 보내기

링크를 클릭하면 나오는 이 주소를 포스트맨에 입력하고, 적절한 값을 넣어줍니다.

[번외] localhost:8080 → dev.travel-planner.xyz 로 변경했을때

제대로 동작하는지 vue.js 를 이용해 간단히 작성해보는 방법을 안내하는 글을 작성해보았습니다! 앞으로 프론트팀에게 전달하기 전에 한번씩 테스트 하는것도 나쁘지 않은것 같습니다.

더 생각해보기

@김시은
이거는 크게 중요한건 아닌데… 메일올때 프로필 이미지랑, 메일 내용에 html 태그를 반환해서 좀 이쁘게 만들었으면 좋겠습니다… 너무 스팸메일 같아 보여요…
@임준형 redis 유효시간을 담아서 보내줘도 좋을것같네요! 임시토큰 유효시간으로 담아두면 좋을 것 같아요