이번에는 비밀번호를 잊어버렸을 때 새 비밀번호를 발급받는 기능을 구현해본다.
난수를 이용해서 새 비밀번호를 생성하고 메일로 발송하는 과정을 알아본다.
새 비밀번호 생성 기능의 개요
암호화된 비밀번호는 데이터베이스 관리자도 알 수 없다. 따라서 새로운 비밀번호를 생성한 후 데이터베이스에 업데이트하고 관리자에게 메일로 발송해야 한다.
메일 발송 모듈 설정하기
스프링에서는 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;
}
이제 새 비밀번호 생성 및 메일 발송 기능이 정상적으로 수행되는지 확인해본다.
![]() |
![]() |
![]() |
메일로 전송된 새로운 비밀번호를 확인한다.
메일로 받은 새로운 비밀번호를 이용해서 로그인을 하고, 로그인 성공 메시지를 확인한다.
![]() |
![]() |
'Spring & Springboot > 올인원 스프링 프레임워크' 카테고리의 다른 글
전자 도서관 프로젝트 - 도서 검색 기능 구현 (0) | 2023.11.30 |
---|---|
전자 도서관 프로젝트 - 신규 도서 등록 기능 구현 (0) | 2023.11.29 |
전자 도서관 프로젝트 - 관리자 계정 정보 수정 기능 구현 (0) | 2023.11.28 |
전자 도서관 프로젝트 - 일반 관리자 승인 처리 기능 구현 (0) | 2023.10.12 |
전자 도서관 프로젝트 - 로그인 상태 유지 및 로그아웃 기능 구현 (0) | 2023.10.11 |