Spring & Springboot/올인원 스프링 프레임워크

전자 도서관 프로젝트 - 관리자 새 비밀번호 생성 기능 구현

YJ_ma 2023. 11. 29. 00:28

이번에는 비밀번호를 잊어버렸을 때 새 비밀번호를 발급받는 기능을 구현해본다.

난수를 이용해서 새 비밀번호를 생성하고 메일로 발송하는 과정을 알아본다.

 

새 비밀번호 생성 기능의 개요

암호화된 비밀번호는 데이터베이스 관리자도 알 수 없다. 따라서 새로운 비밀번호를 생성한 후 데이터베이스에 업데이트하고 관리자에게 메일로 발송해야 한다.

메일 발송 모듈 설정하기

스프링에서는 JavaMailSenderImpl을 이용해서 메일을 발송할 수 있다. JavaMailSenderI을 이용하기 위해서는 pom.xml에 관련 모듈을 설정하고 JavaMailSenderImpl을 스프링 IoC 컨테이너에 등록하는 작업을 해야한다.

① pom.xml 파일을 열어서 다음 코드를 <dependencies> ... </dependencies>에 추가한다.

<!-- SEND MAIL -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.sun.mail/javax.mail -->
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>javax.mail</artifactId>
    <version>1.6.2</version>
</dependency>

 

② [spring] 폴더에 mail-context.xml 파일을 만들고 JavaMailSenderImpl 빈을 생성하는 코드를 작성한다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
	<!-- JavaMailSenderImpl 객체 -->
	<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
		<property name="host" value="smtp.gmail.com" />
		<property name="port" value="587" />
		<property name="username" value="나의 구글 계정 아이디" />  
		<property name="password" value="구글 계정 비밀번호" />    
		<property name="javaMailProperties">
			<props>
				<prop key="mail.smtp.auth">true</prop>
				<prop key="mail.smtp.starttls.enable">true</prop>
			</props>
		</property>
	</bean>
	
</beans>

 

· host는 보내는 메일 서버를 gmil로 설정한다.

· port는 포트번호를 설정한다.

· username은 gmail 계정을 설정한다. 본인의 gmail 계정을 입력해준다.

· password는 잠시 후 생성하는 구글 앱 비밀번호를 입력해준다.

 

③ 프로젝트 서버가 실행될 때 mail-context.xml을 인식할 수 있도록 web.xml 파일을 열어서 <context-param>에 mail-context.xml을 추가해준다.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/spring/root-context.xml
    /WEB-INF/spring/jdbc-context.xml
    /WEB-INF/spring/security-context.xml
    /WEB-INF/spring/mail-context.xml
    </param-value>
</context-param>

 

Gmail 2단계 인증 및 앱 비밀번호 생성하기

① 크롬에서 구글 계정으로 로그인 후 'Google 계정 관리'를 클릭한다.

 

② 왼쪽 카테고리에서 '보안'을 클릭하고, Google에 로그인하는 방법에서 '2단계 인증'을 클릭한다.

 

③ 2단계 인증으로 계정 보호 창이 뜨면 <시작하기>를 클릭한다. 구글 계정으로 로그인을 하고 핸드폰으로 인증코드를 받으면 코드를 입력하고 <다음>을 클릭한다.

④ '사용설정'을 클릭해서 2단계 인증을 완료한다. 2단계 인증이 완료되면, 스크롤을 내려 페이지 하단의 '앱 비밀번호'를 만든다.

 

⑤ 기기 선택에 '기타(맞춤 이름)'를 선택하고 'spring app'이라고 직접 입력한다.

⑥ 앱 비밀번호가 생성되면 16자리 비밀번호를 복사해 mail-context.xml의 password에 붙여넣는다.

 

<property name="password" value="qsvfzlaphflciprz" />

 

새 비밀번호를 메일로 발송하기

이제 새로운 비밀번호 생성과 메일 발송을 구현해본다. 

<find password> 버튼은 /admin/member/findPasswordForm에 링크되어 있다.

<a href="<c:url value='/admin/member/findPasswordForm' />">find password</a>

 

AdminMemberController에 findPasswordForm()을 선언하고 /admin/member/findPasswordForm을 매핑한다.

// 비밀번호 찾기
@GetMapping("/findPasswordForm")
public String findPasswordForm() {
    System.out.println("[AdminMemberController] findPasswordForm()");

    String nextPage = "admin/member/find_password_form";

    return nextPage;
}

 

find_password_form.jsp에서는 관리자의 아이디, 이름, 메일 주소를 서버(/admin/member/findPasswordConfirm)로 전송한다.

경로 : webapp\WEB-INF\views\admin\member\find_password_form.jsp

..생략..
<form action="<c:url value='/admin/member/findPasswordConfirm' />" name="find_password_form" method="post">

    <input type="text" name="a_m_id" placeholder="INPUT ADMIN ID."> <br>
    <input type="text" name="a_m_name" placeholder="INPUT ADMIN NAME."> <br>
    <input type="text" name="a_m_mail" placeholder="INPUT ADMIN MAIL."> <br>
    <input type="button" value="find password" onclick="findPassword();"> 
    <input type="reset" value="reset">

</form>
..생략..

 

AdminMemberController에 findPasswordConfirm()을 선언하고 /findPasswordConfirm을 매핑한다.

// 비밀번호 찾기 확인
@PostMapping("/findPasswordConfirm")
public String findPasswordConfirm(AdminMemberVo adminMemberVo) {
    System.out.println("[AdminMemberController] findPasswordConfirm()");

    String nextPage = "admin/member/find_password_ok";

    int result = adminMemberService.findPasswordConfirm(adminMemberVo);

    if (result <= 0)
        nextPage = "admin/member/find_password_ng";

    return nextPage;
}

 

AdminMemberService에 findPasswordConfirm()을 이용해서 새로운 비밀번호를 생성한다.

public int findPasswordConfirm(AdminMemberVo adminMemberVo) {
    System.out.println("[AdminMemberService] findPasswordConfirm()");

    AdminMemberVo selectedAdminMemberVo = adminMemberDao.selectAdmin(adminMemberVo.getA_m_id(),
            adminMemberVo.getA_m_name(), adminMemberVo.getA_m_mail());

    int result = 0;

    if (selectedAdminMemberVo != null) {
        String newPassword = createNewPassword();
        result = adminMemberDao.updatePassword(adminMemberVo.getA_m_id(), newPassword);

        if (result > 0)
            sendNewPasswordByMail(adminMemberVo.getA_m_mail(), newPassword);
    }
    return result;
}

 

· AdminMemberDao의 selectAdmin()을 이용해서 관리자 인증을 한다. 관리자가 입력한 아이디, 이름, 메일 주소와 일치하는 관리자가 데이터베이스에 있다면 등록된 관리자로 인증한다.

· createNewPassword()는 난수를 이용해서 새로운 비밀번호를 생성한다.

· AdminMemberDao의 updatePassword()를 이용해서 새로운 비밀번호로 업데이트 한다. 다시 말해 데이터베이스의 관리자 암호를 새로운 비밀번호로 업데이트하고, 결과를 result에 반환한다.

· 데이터베이스의 비밀번호 업데이트가 정상적으로 완료됐다면, sendNewPasswordByMail()을 이용해서 관리자 메일로 새로운 비밀번호를 발송한다.

 

스프링 IoC컨테이너에 생성된 JavaMailSenderImpl 빈을 사용하기 위해서 AdminMemberService에 @Autowired를 이용해서 자동 주입한다. 그리고 새로운 비밀번호를 생성하는 createNewPassword()를 선언한다.

@Autowired
JavaMailSenderImpl javaMailSenderImpl;
..생략..
private String createNewPassword() {
    System.out.println("[AdminMemberService] createNewPassword()");
    char[] chars = new char[] {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z'};
    StringBuffer stringBuffer = new StringBuffer();
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.setSeed(new Date().getTime());

    int index = 0;
    int length = chars.length;
    for (int i = 0; i < 8; i++) {
        index = secureRandom.nextInt(length);

        if (index % 2 == 0)
            stringBuffer.append(String.valueOf(chars[index]).toUpperCase());
        else
            stringBuffer.append(String.valueOf(chars[index]).toLowerCase());
    }

    System.out.println("[AdminMemberService] NEW PASSWORD: " + stringBuffer.toString());

    return stringBuffer.toString();
}

 

· secureRandom : java.security.SecureRandom 패키지에 있는 클래스로 Random보다 강력한 난수를 생성한다.

· index가 짝수이면 대문자로 변경하고 홀수이면 소문자로 변경한다.

 

메일을 발송하는 sendNewPasswordByMail()은 다음과 같다.

private void sendNewPasswordByMail(String toMailAddr, String newPassword) {
    System.out.println("[AdminMemberService] sendNewPasswordByMail()");

    final MimeMessagePreparator mimeMessagePreparator = new MimeMessagePreparator() {

        @Override
        public void prepare(MimeMessage mimeMessage) throws Exception {
            final MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
            mimeMessageHelper.setTo(toMailAddr);
            mimeMessageHelper.setSubject("[한국도서관] 새비밀번호 안내입니다.");
            mimeMessageHelper.setText("새비밀번호: " + newPassword, true);
        }
    };
    javaMailSenderImpl.send(mimeMessagePreparator);
}

· MimeMessagePreparator를 구현한 익명 클래스를 생성한다.

· prepare()에 받은 메일 주소, 제목, 본문 데이터를 설정한다.

mimeMessageHelper.setTo(받은 메일 주소);
mimeMessageHelper.setSubject(제목);
mimeMessageHelper.setText(내용);

 

이제 DAO에서 관리자를 인증하는 selectAdmin()과 새 비밀번호로 업데이트하는 updatePassword()를 선언한다.

public AdminMemberVo selectAdmin(String a_m_id, String a_m_name, String a_m_mail) {
    System.out.println("[AdminMemberDao] selectAdmin()");

    String sql = "SELECT * FROM tbl_admin_member " + "HWERE a_m_id = ? AND a_m_name = ? AND a_m_mail = ?";

    List<AdminMemberVo> adminMemberVos = new ArrayList<AdminMemberVo>();

    try {
        adminMemberVos = jdbcTemplate.query(sql, new RowMapper<AdminMemberVo>() {

            @Override
            public AdminMemberVo mapRow(ResultSet rs, int rowNum) throws SQLException {
                AdminMemberVo adminMemberVo = new AdminMemberVo();

                adminMemberVo.setA_m_no(rs.getInt("a_m_no"));
                adminMemberVo.setA_m_approval(rs.getInt("a_m_approval"));
                adminMemberVo.setA_m_id(rs.getString("a_m_id"));
                adminMemberVo.setA_m_pw(rs.getString("a_m_pw"));
                adminMemberVo.setA_m_name(rs.getString("a_m_name"));
                adminMemberVo.setA_m_gender(rs.getString("a_m_gender"));
                adminMemberVo.setA_m_part(rs.getString("a_m_part"));
                adminMemberVo.setA_m_position(rs.getString("a_m_position"));
                adminMemberVo.setA_m_mail(rs.getString("a_m_mail"));
                adminMemberVo.setA_m_phone(rs.getString("a_m_phone"));
                adminMemberVo.setA_m_reg_date(rs.getString("a_m_reg_date"));
                adminMemberVo.setA_m_mod_date(rs.getString("a_m_mod_date"));

                return adminMemberVo;
            }
        }, a_m_id, a_m_name, a_m_mail);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return adminMemberVos.size() > 0 ? adminMemberVos.get(0) : null;
}

public int updatePassword(String a_m_id, String newPassword) {
    System.out.println("[AdminMemberDao] updatePassword()");

    String sql = "UPDATE tbl_admin_member SET " + "a_m_pw = ?, a_m_mod_date = NOW() " + "WHERE a_m_id = ?";

    int result = -1;

    try {
        result = jdbcTemplate.update(sql, passwordEncoder.encode(newPassword), a_m_id);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;

}

 

 

이제 새 비밀번호 생성 및 메일 발송 기능이 정상적으로 수행되는지 확인해본다.

메일로 전송된 새로운 비밀번호를 확인한다.

메일로 받은 새로운 비밀번호를 이용해서 로그인을 하고, 로그인 성공 메시지를 확인한다.