회원가입 인증메일(네이버)

InitSave·2024년 5월 6일

인증메일을 하는 이유는 무분별한 사용자들의 접속을 막을 수 있고 원치 않는 액세스를 방지 할 수 있다. 또한 온라인 서비스에서 보안 및 사용자에 대한 신원 확인이 가능하다.

인증메일을 보내는 시점은 ?

회원가입 양식 입력 후 member테이블 저장 할때 member_auth 테이블에도 저장 해주고, 인증메일 유효시간도 지정해주자

회원가입 인증메일 보내는 건 어떻게 해야 할까?

  1. DB 테이블 생성(member_auth)

    컬럼명Comment
    auth_seq자동시퀀스(AI)
    member_seq회원번호
    auth_num인증문자
    auth_uri인증용난수(링크)
    reg_dtm등록일자
    expire_dtm인증유효시간
    auth_yn인증여부(Y/N)
  2. 기존 프로젝트에서 사용하던 JoinDao → MemberDao(Alt+Shift+r) 수정

  • 변경 이유는 member 테이블을 사용하기 때문이다.
    package com.portfolio.www.dao;
    
    import java.util.HashMap;
    import javax.sql.DataSource;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class MemberDao extends JdbcTemplate {
    	
    	public MemberDao() {}
    	
    	public MemberDao(DataSource ds) {
    		super(ds);
    	}
    	
    	public int join(HashMap<String,String> params) {
    		
    		String sql = "INSERT INTO forum.`member` "
    				+ "( member_id, passwd, member_nm, email, auth_yn, pwd_chng_dtm, join_dtm) "
    				+ "VALUES( '" + params.get("memberId") 
    				+ "', '" + params.get("passwd")
    				+ "', '', '" + params.get("email")
    				+ "', '', '', DATE_FORMAT(now(),'%Y%m%d%H%i%s'))";
    		
    		return update(sql);
    	}
    	
    	public int getmemberSeq(String memberId) {
    		String sql = " SELECT member_seq FROM member WHERE member_id = '" + memberId + "'";
    		
    		return queryForObject(sql, Integer.class);
    		/* queryForObject
    			This method is useful for running static SQL with a known outcome.
    			The query is expected to be a single row/single column query; 
    			the returnedresult will be directly mapped to the corresponding 
    			object type.
    		*/
    	}
    }
    • getmemberSeq : member_auth 테이블에 추가 하기 위해 memberId 값을 조회/사용
    • 수동 빈 등록 된 부분도 모두 수정해주자.(context-root.xml, service)
      	<bean id="joinService" class="com.portfolio.www.service.JoinService" >
      	 	<property name="memberDao" ref="memberDao" />
      	 </bean>
      	
      	<bean id="memberDao" class="com.portfolio.www.dao.MemberDao" >
      		<constructor-arg ref="dataSource" />
      	</bean>
      	private MemberDao memberDao;
      	
      	public void setJoinDao(MemberDao memberDao) {
      		System.out.println("\n\njoinService : " + memberDao + "in joinService \n\n");
      		
      		this.memberDao = memberDao;
      	}
  1. MemberAuthDto 생성
    member_auth 테이블의 컬럼들로 dto를 구성
    - getter / setter로 캡슐화
    package com.portfolio.www.dto;
    
    public class MemberAuthDto {
    	private int authSeq;
    	private int memberSeq;
    	private String authNum;
    	private String authUri;
    	private String regDtm;
    	/* db타입은 bigint인데 자바에서 타입이 long인 이유는?
    	   bigint 타입의 시간데이터는 자바에서 일반적으로 사용하며, 
    	 * 데이터 유지 보수성과 코드 일관성을 유지하기 위한것 */	
    	private long expireDtm; // 시간
    	private String authYn;
    	
    	public int getAuthSeq() {
    		return authSeq;
    	}
    	public void setAuthSeq(int authSeq) {
    		this.authSeq = authSeq;
    	}
    	public int getMemberSeq() {
    		return memberSeq;
    	}
    	public void setMemberSeq(int memberSeq) {
    		this.memberSeq = memberSeq;
    	}
    	public String getAuthNum() {
    		return authNum;
    	}
    	public void setAuthNum(String authNum) {
    		this.authNum = authNum;
    	}
    	public String getAuthUri() {
    		return authUri;
    	}
    	public void setAuthUri(String authUri) {
    		this.authUri = authUri;
    	}
    	public String getRegDtm() {
    		return regDtm;
    	}
    	public void setRegDtm(String regDtm) {
    		this.regDtm = regDtm;
    	}
    	public long getExpireDtm() {
    		return expireDtm;
    	}
    	public void setExpireDtm(long expireDtm) {
    		this.expireDtm = expireDtm;
    	}
    	public String getAuthYn() {
    		return authYn;
    	}
    	public void setAuthYn(String authYn) {
    		this.authYn = authYn;
    	}
    }
  1. MemberAuthDao 생성

    package com.portfolio.www.dao;
    
    import java.util.List;
    import javax.sql.DataSource;
    import org.springframework.jdbc.core.JdbcTemplate;
    import com.portfolio.www.dto.MemberAuthDto;
    import com.portfolio.www.dto.MemberAuthRowMapper;
    
    public class MemberAuthDao extends JdbcTemplate {
    	
    	public MemberAuthDao() {}
    	
    	public MemberAuthDao(DataSource ds) {
    		super(ds);
    	}
    	
    	public int addAuthInfo(MemberAuthDto dto) {
    		String sql = " INSERT INTO forum.member_auth "
    				+ " (member_seq, auth_num, auth_uri, reg_dtm, expire_dtm, auth_yn) "
    				+ " VALUES(" + dto.getMemberSeq()
    				+ " , '', '" + dto.getAuthUri()
    				+ "', DATE_FORMAT(now(),'%Y%m%d%H%i%s'), "
    				+ "" + dto.getExpireDtm()
    				+ ", 'N'); ";
    		
    		return update(sql);
    	}
    	
    	public MemberAuthDto getMemberAuthDto(String uri) {
    		String sql = " SELECT auth_seq, member_seq, auth_num, auth_uri, reg_dtm, expire_dtm, auth_yn "
    				+ " FROM forum.member_auth "
    				+ " WHERE auth_uri = '"+uri+"'"
    				+ " AND auth_yn = 'N';";
    		
    		return query(sql, new MemberAuthRowMapper());
    		
    		/*
    			return queryForObject(sql,MemberAuthDto.class);
    			사용이 안되는것이다 !! 가능한것은 기본형 레퍼 클래스만 가능하다 
    			// 기본형을 위한 single row/column 
    		 */
    	}	
    }
    • 수동 빈 등록 된 부분도 모두 수정해주자.(context-root.xml, service)
      	<bean id="memberAuthDao" class="com.portfolio.www.dao.MemberAuthDao" >
      		<constructor-arg ref="dataSource" />
      	</bean>
      	private MemberAuthDao memberAuthDao;
      	
      	public void setMemberAuthDao(MemberAuthDao memberAuthDao) {
      		this.memberAuthDao = memberAuthDao;
      	}
  1. 인증 메일 보내기 (MailDto 생성)
  • getter / setter
    package com.portfolio.www.dto;
    
    public class EmailDto {
    
    	  private String from;
    	  private String receiver;
    	  private String text;
    	  private String subject;
    	  
    	public String getFrom() {
    		return from;
    	}
    	public void setFrom(String from) {
    		this.from = from;
    	}
    	public String getReceiver() {
    		return receiver;
    	}
    	public void setReceiver(String receiver) {
    		this.receiver = receiver;
    	}
    	public String getText() {
    		return text;
    	}
    	public void setText(String text) {
    		this.text = text;
    	}
    	public String getSubject() {
    		return subject;
    	}
    	public void setSubject(String subject) {
    		this.subject = subject;
    	}
    	  
    }
  1. 인증 메일 보내기 (EmailUtil생성)
    • 이메일 라이브러리 추가
      	<!--이메일 전송을 위한 라이브러리 추가-->
      	<dependency>
      	    <groupId>org.springframework</groupId>
      	    <artifactId>spring-context-support</artifactId>
      	    <version>5.3.34</version>
      	</dependency>
      	
      	<dependency>
      	    <groupId>com.sun.mail</groupId>
      	    <artifactId>javax.mail</artifactId>
      	    <version>1.6.2</version>
      	</dependency>
      • 좀 더 편리하게 version 관리하기
        properties 를 사용해서 해당 spring 버전을 관리하자
        ```xml
        <properties>
            <spring.version>5.3.34</spring.version>
        </properties>
        
          <dependencies>
        	
        	<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-web</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>  
        	
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-context</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>  
        	
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-webmvc</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>  
        	
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-beans</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>  
        	
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-jdbc</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>  
        	
        	<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
        	<dependency>
        	    <groupId>com.mysql</groupId>
        	    <artifactId>mysql-connector-j</artifactId>
        	    <version>8.3.0</version>
        	</dependency>
        	
        	<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
        	<dependency>
        	    <groupId>ch.qos.logback</groupId>
        	    <artifactId>logback-classic</artifactId>
        	    <version>1.5.5</version>
        	</dependency>
        	
        	<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
        	<dependency>
        	    <groupId>ch.qos.logback</groupId>
        	    <artifactId>logback-core</artifactId>
        	    <version>1.5.5</version>
        	</dependency>
        
        	<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        	<dependency>
        	    <groupId>org.slf4j</groupId>
        	    <artifactId>slf4j-api</artifactId>
        	    <version>2.0.13</version>
        	</dependency>
        
        	<!-- https://mvnrepository.com/artifact/at.favre.lib/bcrypt -->
        	<dependency>
        	    <groupId>at.favre.lib</groupId>
        	    <artifactId>bcrypt</artifactId>
        	    <version>0.10.2</version>
        	</dependency>
        
        	<!--이메일 전송을 위한 라이브러리 추가-->
        	<dependency>
        	    <groupId>org.springframework</groupId>
        	    <artifactId>spring-context-support</artifactId>
        	    <version>${spring.version}</version>
        	</dependency>
        	
        	<dependency>
        	    <groupId>com.sun.mail</groupId>
        	    <artifactId>javax.mail</artifactId>
        	    <version>1.6.2</version>
        	</dependency>
        	
          </dependencies>
        ```
    • bean 등록 (context-root.xml 추가)
        	<!-- 이메일 전송하기 위한 bean설정 -->
        	<!-- bean id 가 mailSender 를 이용하기 위해 설정  -->
      	<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
      		<property name="host" value="smtp.naver.com" />
      		<property name="port" value="587" />
      		<property name="username" value="네이버ID" />
      		<property name="password" value="네이버PWD" />
      		<property name="javaMailProperties">
      			<props>
      				<prop key="mail.smtp.starttls.enable">true</prop>
      			</props>
      		</property>
      	</bean>
    • EmailUtil 생성
      package com.portfolio.www.util;
      
      import javax.mail.internet.MimeMessage;
      import org.springframework.mail.javamail.JavaMailSender;
      import org.springframework.mail.javamail.MimeMessageHelper;
      import com.portfolio.www.dto.EmailDto;
      
      public class EmailUtil {
      	 // property 설정으로 받아보기
          private JavaMailSender mailSender;
          
          public void setMailSender(JavaMailSender mailSender) {
      			this.mailSender = mailSender;
      		}
          
          public String sendMail(EmailDto email) {
          	return sendMail(email,false);    	
          }
          
          public String sendMail(EmailDto email, boolean isHtml) {
              try {
                  MimeMessage message = mailSender.createMimeMessage();
                  MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "UTF-8");
                  messageHelper.setTo(email.getReceiver());
                  messageHelper.setFrom(email.getFrom());
                  messageHelper.setSubject(email.getSubject());	// 메일제목은 생략이 가능하다
      
                  messageHelper.setText(email.getText(),isHtml);
                  
                  mailSender.send(message);
      
              } catch(Exception e){
                  System.out.println(e);
                  return "Error";
              }
              return "Sucess";
          }
      	
      	public boolean checkHtml(String link) {
      		return link.contains("<") ? true : false;
      	}
      }
      • bean 등록 (context-root.xml)
        	<bean id="emailUtil" class="com.portfolio.www.util.EmailUtil" >
        	 	<property name="mailSender" ref="mailSender" />
        	 </bean>
  1. JoinService 작성

    package com.portfolio.www.service;
    
    import java.util.Calendar;
    import java.util.HashMap;
    import java.util.UUID;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.portfolio.www.dao.MemberAuthDao;
    import com.portfolio.www.dao.MemberDao;
    import com.portfolio.www.dto.EmailDto;
    import com.portfolio.www.dto.MemberAuthDto;
    import com.portfolio.www.util.EmailUtil;
    
    import at.favre.lib.crypto.bcrypt.BCrypt;
    
    public class JoinService {
    	
    	private static final Logger log = LoggerFactory.getLogger(JoinService.class);
    	
    	private MemberDao memberDao;	
    	private MemberAuthDao memberAuthDao;
    	private EmailUtil emailUtil;
    	
    	public void setMemberDao(MemberDao memberDao) {		
    		this.memberDao = memberDao;
    	}
    	
    	public void setMemberAuthDao(MemberAuthDao memberAuthDao) {
    		this.memberAuthDao = memberAuthDao;
    	}
    	
    	public void setEmailUtil(EmailUtil emailUtil) {
    		this.emailUtil = emailUtil;
    	}
    	
    	public int join(HashMap<String,String> params) {
    		String passwd = params.get("passwd");
    		
    		String encPasswd  = BCrypt.withDefaults().hashToString(12,passwd.toCharArray());
    		log.info("encPasswd >>>>>>>>> " + encPasswd);
    		
    		BCrypt.Result result = BCrypt.verifyer().verify(passwd.toCharArray(), encPasswd);
    		log.info("result.verified >>>>>>> " + result.verified);
    		
    		params.put("passwd", encPasswd);
    		
    		int cnt = memberDao.join(params);
    		
    		/* member 테이블 추가 되었으면 auth 테이블에 추가 하기 위해 구현 */
    		int memberSeq = memberDao.getmemberSeq(params.get("memberId"));
    		
    		if(cnt ==1) {
    			/* 인증 메일구조 만들기 */
    			MemberAuthDto authDto = new MemberAuthDto();
    			// memberSeq 
    			authDto.setMemberSeq(memberSeq);
    			
    			// UUID 사용해서 유니크함을 부여			
    			authDto.setAuthUri(UUID.randomUUID().toString().replaceAll("-", ""));
    			
    			// 인증유효시간
    			Calendar cal = Calendar.getInstance();
    			cal.add(Calendar.MINUTE, 30); // cal 현재시간 기준 (분) + 30
    			
    			authDto.setExpireDtm(cal.getTimeInMillis()); // long 타입으로 반환,(주어진 날짜or시간을 밀리초 단위로)
    			
    			// set된 값을 보기 위한 log
    			log.info("getExpireDtm() : " + authDto.getExpireDtm());
    			
    			memberAuthDao.addAuthInfo(authDto);
    			
    			/* 인증 메일 발송 */
    			EmailDto email = new EmailDto();
    			email.setFrom("네이버ID");
    			email.setReceiver(params.get("email"));
    			email.setSubject("인증하세요");
    			
    			String html = "<a href='http://localhost:8080/04/emailAuth.do?uri="+authDto.getAuthUri()+"'>인증하기</a>";
    			log.info("html : " + html);
    			
    			email.setText(html);
    			
    			emailUtil.sendMail(email,true);			
    		}
    		
    		return cnt;
    	}
    }
    
  • 인증용 난수 UUID를 사용하는 이유는 ? 고유성(유니크함), 예측 불가능성, 표준화, 보안을 위해서 사용한다. 만약 UUID가 생성했는데 중복 가능성이 있다면 UUID + UUID 로 값을 지정하자
  • 유효시간
    현재 기준 long 타입으로 반환해서 값을 지정
  • 이메일 내용
    회원가입 유저가 인증 할 때 편의를 위해서 html 형식으로 인식해서 전송 되도록 하자
  1. 회원가입 아이디 중복 방지를 위해 DB 변경

    member 테이블의 member_id 를 unique key 로 변경

  • 클립보드 히스토리 (윈도우key + v)
  • 메서드의 이름이 같고 매개변수가 다르면 오버로딩, 처음에 하나만 만들었다가 사용하다 보니 사용 case가 많아서 여러 case를 다 살려주기 위해서 오버로딩을 사용. 오버라이딩은 case는 하나밖에 없고 마음대로 구현, 서로 의미가 다르고 오버로딩이 좀 더 자유롭다

0개의 댓글