안경잡이개발자

728x90
반응형

  이번 시간은 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 여섯 번째 시간입니다. 지난 시간에는 회원 데이터를 모델링하는 시간을 가져보았습니다. 이번 시간에는 모델링한 DTO, DAO를 이용하여 실제로 회원가입을 수행하고, 이메일을 전송하는 기능을 구현해보도록 하겠습니다. 기본적으로 이러한 기능을 위해서는 두 가지 유틸리티가 필요합니다. 하나는 회원가입 및 이메일 인증에 사용할 해시 데이터를 생성할 수 있는 SHA256 클래스이고, 다른 하나는 사용자에게 SMTP를 이용해 이메일 인증 메시지를 전송하는 이메일 전송 관련 Gmail 클래스입니다. 바로 다음과 같이 util 패키지를 생성해서 두 개의 클래스를 만들어주시면 됩니다.



  이후에 SMTP를 이용해 메일을 전송하기 위해서는 몇 가지 추가적인 외부 라이브러리가 필요합니다. 첫 번째는 Mail 라이브러리이며 다른 하나는 Activation 클래스입니다. 라이브러리가 없으신 분들은 다음의 링크에서 받아서 이용하시면 됩니다.


  ▶ Mail 라이브러리 다운로드 사이트: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eeplat-419426.html#javamail-1.4.7-oth-JPR


  ▶ Activation 라이브러리 다운로드 사이트: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jaf-1.1-fr-oth-JPR


  위 사이트로 접속하셔서 각각 javamail.zip과 jaf.zip를 다운로드 받으신 뒤에 압축을 푸셔서 라이브러리를 찾아서 다음과 같이 WEB-INF 폴더 내부의 lib 폴더에 넣어주시면 됩니다.



  이제 한 번 우리의 util 패키지 내에 있는 클래스들을 작성해보도록 하겠습니다.


  ▶ SHA256.java


package util;


import java.security.MessageDigest;


public class SHA256 {


public static String getSHA256(String input) {

StringBuffer result = new StringBuffer();

try {

MessageDigest digest = MessageDigest.getInstance("SHA-256");

byte[] salt = "Hello! This is Salt.".getBytes();

digest.reset();

digest.update(salt);

byte[] chars = digest.digest(input.getBytes("UTF-8"));

for(int i = 0; i < chars.length; i++) {

String hex = Integer.toHexString(0xff & chars[i]);

if(hex.length() == 1) result.append('0');

result.append(hex);

}

} catch (Exception e) {

e.printStackTrace();

}

return result.toString();

}

}


  위 소스코드를 보시면 솔트(Salt) 부분이 들어가있는 것을 알 수 있습니다. 해시 데이터를 생성할 때 악의적인 목적을 가진 공격자가 원래의 데이터를 파악하기 더욱 어렵게 만들어 줄 수 있습니다.


  ▶ Gmail.java


package util;


import javax.mail.Authenticator;

import javax.mail.PasswordAuthentication;


public class Gmail extends Authenticator {


    @Override

    protected PasswordAuthentication getPasswordAuthentication() {

        return new PasswordAuthentication("아이디","비밀번호");

    }

    

}


  SMTP에서 인증을 위한 부분을 위와 같이 클래스에서 작성할 수 있습니다. 저는 구글 이메일(G-Mail)을 사용해보도록 하겠습니다. 자신의 구글 계정 중에서 이메일을 사용할 수 있는 계정을 입력하시면 됩니다. 또한 기본적으로 구글 계정은 '계정' 설정에서 '보안 수준이 낮은 앱'에 대한 접근 권한이 설정되어야 합니다.



  이제 다음과 같이 userRegisterAction.jsp, emailCheckAction.jsp, emailSendAction.jsp 세 가지를 만들어줍니다.



▶ userRegisterAction.jsp: 회원가입을 처리합니다. 사용자 정보를 데이터베이스에 등록한 이후에, 자동으로 사용자의 이메일로 이메일을 전송하고 인증을 기다립니다.


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="user.UserDTO"%>

<%@ page import="user.UserDAO"%>

<%@ page import="util.SHA256"%>

<%@ page import="java.io.PrintWriter"%>

<%

request.setCharacterEncoding("UTF-8");

String userID = null;

String userPassword = null;

String userEmail = null;

if(request.getParameter("userID") != null) {

userID = (String) request.getParameter("userID");

}

if(request.getParameter("userPassword") != null) {

userPassword = (String) request.getParameter("userPassword");

}

if(request.getParameter("userEmail") != null) {

userEmail = (String) request.getParameter("userEmail");

}

if (userID == null || userPassword == null || userEmail == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('입력이 안 된 사항이 있습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

} else {

UserDAO userDAO = new UserDAO();

int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));

if (result == -1) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('이미 존재하는 아이디입니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

} else {

session.setAttribute("userID", userID);

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("location.href = 'emailSendAction.jsp';");

script.println("</script>");

script.close();

}

}

%>


▶ emailSendAction.jsp: 이메일 인증 메시지를 이메일을 통해 전송합니다.


<%@page import="javax.mail.Transport"%>

<%@page import="javax.mail.Message"%>

<%@page import="javax.mail.Address"%>

<%@page import="javax.mail.internet.InternetAddress"%>

<%@page import="javax.mail.internet.MimeMessage"%>

<%@page import="javax.mail.Session"%>

<%@page import="javax.mail.Authenticator"%>

<%@page import="java.util.Properties"%>

<%@page import="java.io.PrintWriter"%>

<%@page import="user.UserDAO"%>

<%@page import="util.SHA256"%>

<%@page import="util.Gmail"%>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%

UserDAO userDAO = new UserDAO();

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

boolean emailChecked = userDAO.getUserEmailChecked(userID);

if(emailChecked == true) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('이미 인증 된 회원입니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

}

// 사용자에게 보낼 메시지를 기입합니다.

String host = "http://localhost:8080/Lecture_Evaluation/";

String from = "이메일 아이디";

String to = userDAO.getUserEmail(userID);

String subject = "강의평가를 위한 이메일 확인 메일입니다.";

String content = "다음 링크에 접속하여 이메일 확인을 진행하세요." +

"<a href='" + host + "emailCheckAction.jsp?code=" + new SHA256().getSHA256(to) + "'>이메일 인증하기</a>";

// SMTP에 접속하기 위한 정보를 기입합니다.

Properties p = new Properties();

p.put("mail.smtp.user", from);

p.put("mail.smtp.host", "smtp.googlemail.com");

p.put("mail.smtp.port", "465");

p.put("mail.smtp.starttls.enable", "true");

p.put("mail.smtp.auth", "true");

p.put("mail.smtp.debug", "true");

p.put("mail.smtp.socketFactory.port", "465");

p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

p.put("mail.smtp.socketFactory.fallback", "false");

 

try{

    Authenticator auth = new Gmail();

    Session ses = Session.getInstance(p, auth);

    ses.setDebug(true);

    MimeMessage msg = new MimeMessage(ses); 

    msg.setSubject(subject);

    Address fromAddr = new InternetAddress(from);

    msg.setFrom(fromAddr);

    Address toAddr = new InternetAddress(to);

    msg.addRecipient(Message.RecipientType.TO, toAddr);

    msg.setContent(content, "text/html;charset=UTF-8");

    Transport.send(msg);

} catch(Exception e){

    e.printStackTrace();

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('오류가 발생했습니다..');");

script.println("history.back();");

script.println("</script>");

script.close();

    return;

}

%>

<!doctype html>

<html>

  <head>

    <title>강의평가 웹 사이트</title>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- 부트스트랩 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/bootstrap.min.css">

    <!-- 커스텀 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/custom.css">

  </head>

  <body>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">

      <a class="navbar-brand" href="index.jsp">강의평가 웹 사이트</a>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar">

        <span class="navbar-toggler-icon"></span>

      </button>

      <div class="collapse navbar-collapse" id="navbar">

        <ul class="navbar-nav mr-auto">

          <li class="nav-item active">

            <a class="nav-link" href="index.jsp">메인</a>

          </li>

          <li class="nav-item dropdown">

            <a class="nav-link dropdown-toggle" id="dropdown" data-toggle="dropdown">

              회원 관리

            </a>

            <div class="dropdown-menu" aria-labelledby="dropdown">

              <a class="dropdown-item" href="userLogin.jsp">로그인</a>

            </div>

          </li>

        </ul>

        <form action="./index.jsp" method="get" class="form-inline my-2 my-lg-0">

          <input type="text" name="search" class="form-control mr-sm-2" placeholder="내용을 입력하세요.">

          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">검색</button>

        </form>

      </div>

    </nav>

<div class="container">

    <div class="alert alert-success mt-4" role="alert">

  이메일 주소 인증 메일이 전송되었습니다. 이메일에 들어가셔서 인증해주세요.

</div>

    </div>

    <footer class="bg-dark mt-4 p-5 text-center" style="color: #FFFFFF;">

      Copyright ⓒ 2018 나동빈 All Rights Reserved.

    </footer>

    <!-- 제이쿼리 자바스크립트 추가하기 -->

    <script src="./js/jquery.min.js"></script>

    <!-- Popper 자바스크립트 추가하기 -->

    <script src="./js/popper.min.js"></script>

    <!-- 부트스트랩 자바스크립트 추가하기 -->

    <script src="./js/bootstrap.min.js"></script>

  </body>

</html>


▶ emailCheckAction.jsp: 사용자가 이메일 인증을 하게 되면 그에 대한 정보를 처리하는 페이지입니다.


<%@page import="java.io.PrintWriter"%>

<%@page import="util.SHA256"%>

<%@page import="user.UserDAO"%>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%

request.setCharacterEncoding("UTF-8");

String code = request.getParameter("code");

UserDAO userDAO = new UserDAO();

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

String userEmail = userDAO.getUserEmail(userID);

boolean rightCode = (new SHA256().getSHA256(userEmail).equals(code)) ? true : false;

if(rightCode == true) {

userDAO.setUserEmailChecked(userID);

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('인증에 성공했습니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('유효하지 않은 코드입니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

}

%>


  이제 한 번 테스트를 해봅시다.



  먼저 위와 같이 회원가입을 해주겠습니다.



이후에 위와 같이 회원가입 된 정보를 확인합시다.



이후에 이메일 주소 인증 메일이 전송되었다는 메시지가 나오네요.



  이제 이메일 함으로 이동해보시면 위와 같이 인증 메일이 온 것을 확인할 수 있습니다.



  링크를 타고 이동하면 위와 같이 인증이 완료되어 메인 페이지가 등장합니다.



이후에 위와 같이 데이터베이스에서 사용자 정보를 확인하면 'userEmailChecked' 속성의 값이 1(True)로 바뀌어있는 것을 확인할 수 있습니다.

728x90
반응형

728x90
반응형

  이번 강의는 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 다섯 번째 시간입니다. 이번 시간에는 지난 시간에 했었던 데이터베이스 설계를 토대로 서버 프로그램상에서 데이터를 모델링하려고 합니다. 일반적으로 데이터를 모델링할 때는 DTO(Data Transfer Object)와 DAO(Data Access Object)를 작성해주시면 됩니다. DTO는 말 그대로 데이터베이스 테이블과 거의 1:1 대응되어 개별 데이터 원소를 보관하기 위해 사용되는 객체이며 DAO는 데이터베이스와 실질적으로 통신하는 모듈입니다. 이번 시간에는 가장 먼저 회원 데이터를 모델링해보도록 하겠습니다.



  먼저 위와 같이 자바 소스코드 영역에 user 패키지를 생성해서 UserDAO와 UserDTO를 자바 파일로 생성합니다.


  ▶ UserDTO.java


package user;


public class UserDTO {


private String userID;

private String userPassword;

private String userEmail;

private String userEmailHash;

private boolean userEmailChecked;

public String getUserID() {

return userID;

}

public void setUserID(String userID) {

this.userID = userID;

}

public String getUserPassword() {

return userPassword;

}

public void setUserPassword(String userPassword) {

this.userPassword = userPassword;

}

public String getUserEmail() {

return userEmail;

}

public void setUserEmail(String userEmail) {

this.userEmail = userEmail;

}

public String getUserEmailHash() {

return userEmailHash;

}

public void setUserEmailHash(String userEmailHash) {

this.userEmailHash = userEmailHash;

}

public boolean isUserEmailChecked() {

return userEmailChecked;

}

public void setUserEmailChecked(boolean userEmailChecked) {

this.userEmailChecked = userEmailChecked;

}

public UserDTO(String userID, String userPassword, String userEmail,

String userEmailHash, boolean userEmailChecked) {

this.userID = userID;

this.userPassword = userPassword;

this.userEmail = userEmail;

this.userEmailHash = userEmailHash;

this.userEmailChecked = userEmailChecked;

}


}


  위 DTO 파일을 보시면 사실상 이전 시간에 작업했던 데이터베이스 부분과 거의 일치하는 것을 알 수 있습니다. MySQL 언어를 자바 소스코드 언어로 바꾼 것 뿐이지요.


  ▶ UserDAO.java


package user;


import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;


public class UserDAO {


private Connection conn;

private ResultSet rs;


public UserDAO() {

try {

String dbURL = "jdbc:mysql://localhost:3306/LectureEvaluation";

String dbID = "root";

String dbPassword = "root1234";

Class.forName("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection(dbURL, dbID, dbPassword);

} catch (Exception e) {

e.printStackTrace();

}

}

public int login(String userID, String userPassword) {

String SQL = "SELECT userPassword FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

if(rs.next()) {

if(rs.getString(1).equals(userPassword))

return 1; // 로그인 성공

else

return 0; // 비밀번호 틀림

}

return -1; // 아이디 없음

} catch (SQLException e) {

e.printStackTrace();

}

return -2; // 데이터베이스 오류

}

public int join(UserDTO user) {

String SQL = "INSERT INTO USER VALUES (?, ?, ?, ?, false)";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, user.getUserID());

pstmt.setString(2, user.getUserPassword());

pstmt.setString(3, user.getUserEmail());

pstmt.setString(4, user.getUserEmailHash());

return pstmt.executeUpdate();

} catch (SQLException e) {

e.printStackTrace();

}

return -1; // 회원가입 실패

}

public String getUserEmail(String userID) {

String SQL = "SELECT userEmail FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

while(rs.next()) {

return rs.getString(1); // 이메일 주소 반환

}

} catch (SQLException e) {

e.printStackTrace();

}

return null; // 데이터베이스 오류

}

public boolean getUserEmailChecked(String userID) {

String SQL = "SELECT userEmailChecked FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

while(rs.next()) {

return rs.getBoolean(1); // 이메일 등록 여부 반환

}

} catch (SQLException e) {

e.printStackTrace();

}

return false; // 데이터베이스 오류

}

public boolean setUserEmailChecked(String userID) {

String SQL = "UPDATE USER SET userEmailChecked = true WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

pstmt.executeUpdate();

return true; // 이메일 등록 설정 성공

} catch (SQLException e) {

e.printStackTrace();

}

return false; // 이메일 등록 설정 실패

}

}


  UserDAO에서는 실제로 데이터베이스와 통신하는 부분이 들어있습니다. 보시면 생성자는 데이터베이스와 연동하는 부분임을 알 수 있습니다. 나머지 함수들은 각각 데이터베이스와 통신하는 유의미한 함수들입니다.

  - login(): 아이디와 비밀번호를 받아서, 로그인을 시도하는 함수입니다. 결과는 정수형으로 반환됩니다.
  - join(): 사용자의 정보를 입력 받아서 회원가입을 수행하는 함수입니다. 결과는 정수형으로 반환됩니다.

  - getUserEmail(): 사용자의 아이디를 이용해 이메일 주소를 알아냅니다. 결과는 문자열형으로 반환됩니다.

  - getUserEmailChecked(): 사용자가 현재 이메일 인증이 되었는지 확인하는 함수입니다. 결과는 참/값형으로 반환됩니다.
  - setUserEmailChecked(): 특정한 사용자의 이메일 인증을 수행해줍니다.

  또한 사실 이 DAO는 잘못 설계된 소스코드에요. 원래는 conn 객체를 매 함수마다 새롭게 초기화주어 사용하셔야 합니다. 지금 제 DAO 같은 경우는 접속자가 많은 경우 데이터베이스 연결 갯수 초과 오류가 뜰 수 있어요. 데이터베이스 커넥션 풀을 사용하는 방식으로 효과적으로 해결할 수도 있구요. 하지만 저는 이러한 문제를 알고도 그냥 진행하겠습니다. 왜냐면 귀찮거든요. 작은 프로젝트니까 그냥 넘어갈게요. ㅋㅋㅋ


  이제 데이터베이스 연동을 위해서 JDBC Connector를 설치해주도록 할게요. MySQL 전용 드라이버는 다음의 경로에서 구하실 수 있습니다.


  ▶ MySQL JDBC 드라이버 사이트: https://dev.mysql.com/downloads/connector/j/5.1.html



  위와 같이 프로젝트가 구성되도록 JDBC를 넣어주시면 된답니다. WEB-INF 폴더의 lib 폴더에 넣어주시면 자동으로 웹 어플리케이션이 구동되는 단계에서 해당 라이브러리를 사용할 수 있게 되서 문제가 없습니다.

728x90
반응형