gi_dor

비밀번호 찾기 + 임시비밀번호 이메일전송 본문

Back_End/SpringBoot

비밀번호 찾기 + 임시비밀번호 이메일전송

기돌 2024. 4. 30. 12:52

스프링시큐리티에 PasswordEncoder를 통해 DB에 비밀번호는 암호화 되어있다

만약 사용자가  비밀번호를 잃어버렸다면 어떻게 처리해줘야할까 ?

0. 로그인 - 비밀번호를 잃어버렸다.

1. 사용자가 입력한 아이디와 이메일로 사용자 정보를 조회한다 

  • 성공 - POST 
  • 실패 - alert 로 알림창 띄워서 거부하기
    입력한 값이 nul , 비어있는 공백
    일치하지 않는 값으로 인해 사용자 정보 조회가 안될때

2. 임시비밀번호 만들기

  •  Random random 을 사용해 비밀번호 대소문자영어숫자특수문자로 최소8자 최대 16자를 생성하게 만들었다
  • 아이디와 , 새로만들어진 비밀번호로 update 문으로 DB에 비밀번호 변경

3. 임시비밀번호 이메일 보내기

  • MimeMessge 로 이메일 보내기 SimpleMessage를 사용한다면 정말간단한 text문 만 보낼수 있다
  • html 파일을 보낼 것이기에 HtmlTemplate 만들기

4. 컨트롤러에서 처리하기

  • 입력한 id , email 을 사용해 사용자 정보조회
  • 조회한 사용자 정보의 email 로 임시 비밀번호 보내기
  • 완료후 로그인 페이지로 이동 
  • 받은 이메일 확인후 로그인

<a href="/user/login/passwordReset" class="text-center" style="display: block;">비밀번호를 잊으셨나요?</a> 
// 비밀번호 재설정 페이지로 이동하는 핸들러
@GetMapping("/login/passwordReset")
public String showPasswordResetPage() {
    return "user/passwordResetForm"; // 비밀번호 재설정 폼으로 이동
}

 

<div class="row mb-3">
    <div class="col-2"></div>
    <div class="col-9">
        <h4 style="text-align: center">비밀번호 찾기 </h4>
        <br>
        <form class="border bg-light mb-3" method="post" th:action="@{/user/login/passwordReset}">
            <div class="form-group">
                <div class="col-6 mb-3 ms-3 mt-3">
                    <label class="form-label">아이디</label>
                    <input type="text" class="form-control" name="id" />
                </div>
            </div>
            <div class="form-group mb-3 ms-3">
                <label class="form-label">이메일</label>
                <div class="row">
                    <div class="col-3">
                        <input type="text" class="form-control" id="emailId" name="email1"  placeholder="이메일 입력" />
                        <br>
                    </div>
                    @
                    <div class="col-4">
                        <select id="emailAddress" class="form-select" name="email2">
                            <option value="google.com">google.com</option>
                            <option value="naver.com">naver.com</option>
                            <option value="daum.net">daum.net</option>
                        </select>
                    </div>
                    <div class="col-3">
                        <button type="submit" id="resetButton" class="btn btn-secondary">비밀번호 찾기</button>
                    </div>
                </div>
            </div>     <!-- <div class="form-group mb-3"> -->
        </form>
    </div>
    <div class="col-1"></div>
</div>
<select id="selectUserByIdAndEmail" resultType="com.example.bookhub.user.vo.User">
    SELECT
        u.user_no              as no,
           u.user_id              as id,
           u.user_password        as password,
           u.user_name            as name,
           u.user_email           as email ,
           u.user_tel             as tel ,
           u.user_zip_code        as zipCode ,
           u.user_address         as address ,
           u.user_address_detail  as addressDetail ,
           u.user_created_date    as createdDate,
           u.user_updated_date    as updatedDate ,
           u.user_del_yn          as delYn ,
           u.user_point           as point,
           g.grade_no             as "userGrade.no",
           g.grade_name           as "userGrade.name"
    FROM USER u , USER_GRADE g
    WHERE user_id = #{id}
      and user_email = #{email}
      and user_del_yn = 'N'
      and u.grade_no = g.grade_no
</select>


<!-- 랜덤한 비밀번호로 받아서 변경하기 -->
<update id="updateResetPassword">
    UPDATE USER
    SET USER_PASSWORD = #{password}
    WHERE USER_ID = #{id}
</update>
 사실 select 로 저렇게 많은 컬럼 필요 없다  , id 와 pasword , email만 조회해도 된다

 

 

@Mapper
public interface UserMapper {
	User selectUserByIdAndEmail(@Param("id") String id , @Param("email") String email);

	void updateResetPassword(@Param("id") String id, @Param("password") String password);
}
@Service
@RequiredArgsConstructor
public class UserService implements UserDetailsService {

    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    
public User selectUserByIdAndEmail(String id, String email) {
    User user = userMapper.selectUserByIdAndEmail(id,email);
    if (user == null) {
        throw new RuntimeException("해당 이메일에 해당하는 사용자를 찾을 수 없습니다: " + email);
    }
    return user;
}


// 예외 발생하면 롤백되서 이전 상태로 가게하려고 - ACID
@Transactional
public String resetPassword(String id , String email) {

    User user = userMapper.selectUserByIdAndEmail(id, email);

    if (user == null) {
        throw new IllegalArgumentException("주어진 정보에 해당하는 사용자 정보가 없습니다.");
    } else if (id == null || id.isEmpty() || email == null || email.isEmpty()) {
        throw new IllegalArgumentException("아이디와 이메일을 모두 입력해야 합니다.");
    }

    // 임시 비밀번호 만들기
    String resetPassword = RandomPassword.generatePassword(8,16);

    // 비밀번호 변경 - 암호화 안되서 DB에 그냥 비밀번호 저장되버림
    userMapper.updateResetPassword(id,passwordEncoder.encode(resetPassword));

    return resetPassword;
	}
}

passwordEncoder.encode를 해야 임시로받은 비밀번호를 암호화해서 DB에 저장된다 꼭 하자 꼭!!

@PostMapping("/login/passwordReset")
public String resetPassword(@RequestParam("id") String id,
                            @RequestParam("email1") String email1 ,
                            @RequestParam("email2") String email2 ,
                            Model model) {

    try{
        // 입력한 id,email 로 사용자 조회하기
        String email = email1 + "@" + email2;
        User user = userService.selectUserByIdAndEmail(id,email);

        // 임시비밀번호 만들기 +  DB에 update
        String resetPassword = userService.resetPassword(id,email);
        // 임시 비밀번호값 resetPassword
        model.addAttribute("resetPassword",resetPassword);

        // 사용자 조회한 값으로 eamil 보내기
        String to = user.getEmail();
        String subject = "BookHub 임시비밀번호 발급.";
        String html = mailService.resetPasswordTemplate(resetPassword); // loadHtmlTemplate 메서드를 호출할 때는 괄호를 포함하여 호출해야 합니다.
        mailService.sendEmail(to, subject, html);


        return "/user/loginForm";
    } catch (IllegalArgumentException aex) {
        // 사용자 정보가 일치하지 않거나 누락된 경우 에러 메시지를 모델에 추가
        model.addAttribute("error",aex.getMessage());
        return "/user/passwordResetForm";
    }
    catch (Exception ex) {
        return "/main";
    }

}

 

입력한 email1 과 email2를 합쳐 String eamil 에 담은 이유는 User.vo 에  String email; 로 선언되어있기 때문이다

 

1. 입력한 id 와 eamil 로 DB에 저장되어있는 사용자의 정보를 조회한다

User user = userService.selectUserByIdAndEmail(id,email);

2. resetPassword 를 사용해 비밀번호를 새로 발급받은뒤 passwordEncoder 를 사용해 비밀번호를 암호화 처리
     update 로 비밀번호를 변경한다

3. 암호화 처리하기전에  발급받은 임시비밀번호를 eamil에 보내보자

 // 회원가입 완료시에 실행되는 회원가입완료 이메일 보내기
    public void sendEmail(String to, String subject, String html) throws MessagingException {

            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message,true);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(html , true );
            javaMailSender.send(message);
    }


public String resetPasswordTemplate(String password) throws Exception{

    ClassPathResource resource = new ClassPathResource("templates/user/mail/resetPassword.html");
    String htmlTemplate = null;
    try{
        htmlTemplate = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);

    } catch (IOException ex) {
        System.err.println("이메일 템플릿을 로드하는 도중 오류가 발생했습니다.");
        ex.printStackTrace();
    }
    return htmlTemplate.replace("PASSWORD", password);
}

 

 

  • 첫번째 setTo(String... to )  여러명에게  보낼 수 있다
  • 두번째 setTo(String to) 한명에게 보낼 수 있다
public void sendSimpleEmail() {
       
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("임시비밀번호가 도착했습니다");
        message.setTo("rltjs3563@naver.com");
        message.setText("808");

        javaMailSender.send(message);
    }

Spring에서 제공하는 SimpleMailMessage 는  간단한 텍스트 기반 이메일을 생성하는 데 사용된다
복잡한 형식이나 첨부 파일 등을 다루지 않고, 단순한 텍스트 메시지만을 사용할 때 쓰인다

 

MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);

1️⃣ MimeMessage: JavaMail의 MimeMessage 클래스를 확장하여 Spring의 이메일 전송 기능에 특화된 형태로 사용되는데  이메일의 제목, 본문, 첨부 파일 등을 다룰 때 사용한다

2️⃣ MimeMessageHelper: MimeMessage를 더 쉽게 생성하고 조작할 수 있도록 도와주는 헬퍼 클래스MimeMessageHelper는 MimeMessage를 생성하고 이메일의 수신자, 제목, 내용, 첨부 파일 등을 설정하는 데 도움을 준다. 또한 HTML 내용을 지원하고, 첨부 파일을 추가하는 등의 기능을 제공한다

 

resetPassword.html

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>::: 임시비밀번호 발급 :::</title>
</head>
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">

<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 40px; background-color: #f9f9f9;">
    <tr>
        <td align="center" style="max-width: 20%;">
            <!-- 브라우저에서 뜨는 사진에 경로를 입력해야한다고함 + localhost라 안될꺼라고함           -->
            <!--            <img src="/static/image/login/logo.png" alt="BookHub 로고" style="max-width: 100%;">-->
        </td>
    </tr>
    <tr>
        <td align="center">
            <!-- 텍스트로 회사 이름 표시 -->
            <div style="font-size: 24px; font-weight: bold;">BookHub</div>
        </td>
    </tr>
    <tr>
        <td align="center" style="padding: 20px; font-size: 24px; font-weight: bold;">
            임시 비밀번호가 발급 되었습니다.
        </td>
    </tr>
    <tr>
        <td align="center" style="padding: 10px; font-size: 32px; font-weight: bold; color: #333; background-color: #ffffff; border-radius: 5px; margin: 10px;">
            임시비밀번호 : <span >PASSWORD</span>
        </td>

    </tr>
    <tr>
        <td align="center" style="font-size: 16px; padding: 30px;">
            <p>제공해드린 비밀번호는 임시 비밀번호입니다</p>
            <p>로그인 하신후에 마이 페이지에서 비밀번호를 변경한 뒤에 사이트를 이용해주세요</p>
            <p>문의 사항이 있으시면 언제든지 저희에게 연락 주세요.</p>
        </td>
    </tr>
</table>
</body>
</html>

 


https://gi-dor.tistory.com/246

 

[error] update 비밀번호 암호화 문제

🔒 상황계정 비밀번호를 까먹어 이메일로 임시비밀번호를 발급 받았다 하지만 암호화되어버린 비밀번호를 받게되었다 또한 DB에는 반대로 암호화되지 않은 비밀번호가 저장되어있다 https://g.

gi-dor.tistory.com

 

다양한 HTML 템플릿을 원한다면 추천  : https://unlayer.com/templates

 

Free HTML Email Templates and Editor

Browse hundreds of HTML email templates and choose the best for your business. Wide range of templates available for every industry and usage. Start for free.

unlayer.com

 

728x90