간펴니
간편 자바프로그래밍
간펴니
전체 방문자
오늘
어제
  • 전체보기 (185)
    • 알고리즘 (2)
    • JAVA (69)
      • 이펙티브 자바 (47)
      • JAVA 병렬프로그래밍 (5)
      • 자바 (17)
    • SPRING (60)
      • Spring (12)
      • IceWater Community (37)
      • Homme Shop (10)
      • 토비의 스프링 (1)
    • SPRING BOOT (4)
      • WhiteRecord (7)
    • 오류 (9)
    • DB (10)
      • ORACLE (5)
      • MYSQL (1)
      • MYBATIS (4)
      • JPA (0)
      • 대용량 데이터 베이스 (0)
      • SQL (0)
    • FRONT (8)
      • JSP (2)
      • JavaScript (2)
      • Jquery (3)
      • Thymeleaf (1)
    • AWS (6)
    • JNI (10)
    • 회고 (0)
    • MQ (0)
    • Radis (0)
    • Git (0)
    • Docker (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 블로그 컨셉 변경

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
간펴니

간편 자바프로그래밍

[스프링]회원가입 이메일인증 구현
SPRING/IceWater Community

[스프링]회원가입 이메일인증 구현

2021. 9. 19. 20:10
728x90

이번엔 회원가입 시 ,이메일 인증을 구현했다.

메일로 인증번호를 받아서 인증번호를 입력 후 인증하는 방법과

메일에서 바로 버튼을 눌러서 인증을 하는 방법을 생각했는데,

 

사용자가 느끼기엔 버튼을 눌러서 인증하는 방법이 편할 것 같아서 후자의 방법으로 구현했다.

다음 프로젝트땐 전자의 방법으로 구현 할 생각이다. 크게 다르지 않다.

 


pom.xml에 메일 라이브러리를 추가

<!-- mail library -->
<dependency>
	<groupId>javax.mail</groupId>
	<artifactId>mail</artifactId>
	<version>1.4.7</version>
</dependency>

<!-- mail 서포트 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>${org.springframework-version}</version>
</dependency>

MailUtils.java 생성

public class MailUtils {
	private JavaMailSender mailSender;
	private MimeMessage message;
	private MimeMessageHelper messageHelper;
	
	public MailUtils(JavaMailSender mailSender) throws MessagingException {
		this.mailSender = mailSender;
		message = this.mailSender.createMimeMessage();
		messageHelper = new MimeMessageHelper(message, true, "UTF-8");
	}

	public void setSubject(String subject) throws MessagingException {
		messageHelper.setSubject(subject);
	}

	public void setText(String htmlContent) throws MessagingException {
		messageHelper.setText(htmlContent, true);
	}

	public void setFrom(String email, String name) throws UnsupportedEncodingException, MessagingException {
		messageHelper.setFrom(email, name);
	}

	public void setTo(String email) throws MessagingException {
		messageHelper.setTo(email);
	}

	public void addInline(String contentId, DataSource dataSource) throws MessagingException {
		messageHelper.addInline(contentId, dataSource);
	}

	public void send() {
		mailSender.send(message);
	}

}

메일전송 라이브러리의 JavaMailSender인터페이스이다.

위부터,

제목 , 내용, 발송자 , 수신자 , send(보내기) 의 setter이다.

 


tempKey.java 생성

public class TempKey{
	private boolean lowerCheck;
	private int size;
	
	public String getKey(int size, boolean lowerCheck) {
		this.size = size;
		this.lowerCheck = lowerCheck;
		return init();
	}
	
	private String init() {
		Random ran = new Random();
		StringBuffer sb = new StringBuffer();
		int num  = 0;
		do {
			num = ran.nextInt(75) + 48;
			if ((num >= 48 && num <= 57) || (num >= 65 && num <= 90) || (num >= 97 && num <= 122)) {
				sb.append((char) num);
			} else {
				continue;
			}
		} while (sb.length() < size);
		if (lowerCheck) {
			return sb.toString().toLowerCase();
		}
		return sb.toString();
	}
}

이메일 인증코드를 난수화하는 클래스이다.

 

이메일인증 구현 후, 이메일 기능으로 아이디/비밀번호 찾기도 구현할 계획이기 때문에 따로 클래스를 만들어서 

import해서 사용하려고 한다.


servelt-context.xml

<!-- 회원가입 메일 인증 -->
	<beans:bean id="mailSender"	class="org.springframework.mail.javamail.JavaMailSenderImpl">
		<beans:property name="host" value="smtp.gmail.com" />
		<beans:property name="port" value="587" /><!-- 465 or 25 -->
		<beans:property name="username" value="[구글아이디]" />
		<beans:property name="password" value="[구글비밀번호]" />
		<beans:property name="defaultEncoding" value="utf-8" />
		<beans:property name="javaMailProperties">
			<beans:props>
				<beans:prop key="mail.transport.protocol">smtp</beans:prop>
				<beans:prop key="mail.smtp.auth">true</beans:prop>
				<beans:prop key="mail.smtp.starttls.enable">true</beans:prop>
				<beans:prop key="mail.debug">true</beans:prop>
		
			</beans:props>
		</beans:property>
	</beans:bean>

이메일 기능라이브러리의 bean 등록과 설정을 해주자.

 

그리고,

 

https://myaccount.google.com/ 

 

Google 계정

Google은 사용자마다 원하는 개인정보 보호 설정이 다르다는 사실을 인지하고 있습니다. 그래서 Google 계정에서는 사용이 간편한 컨트롤과 개인정보 보호 진단 같은 도구를 제공합니다. 이런 도구

www.google.com

구글 계정설정에 들어가서 보안수준이 낮은 앱의 액세스를 허용해준다.

 

 

혹시,

 

Request processing failed; nested exception is org.springframework.mail.MailSendException: Mail server connection failed; nested exception is javax.mail.MessagingException: Could not convert socket to TLS;

 

에러가 발생하면 

<beans:prop key="mail.smtp.ssl.trust">smtp.gmail.com</beans:prop>
<beans:prop key="mail.smtp.ssl.protocols">TLSv1.2</beans:prop>

 

prop key를 추가해주자.

 

클라이언트(브라우저)와 서버(톰캣)의 버전차이 때문에 발생할 수 있는 문제인데,

버전을 1.2로 올려주면 해결된다.

 

그래도 안되면 방화벽을 끄고 시도해보자.

 


메일주소와 인증키를 저장할 테이블 생성, 회원테이블에 인증키 생성

create table MP_MEMBER_AUTH(MEMBEREMAIL varchar2(100),AUTHKEY varchar2(50));

alter table mp_member add member_auth number default 0;

commit;

 

 


Mapper.xml

<insert id="createAuthKey">
	insert into MP_MEMBER_AUTH values(#{memberEmail}, #{authKey})
</insert>

<update id="memberAuth">
	<![CDATA[update MP_MEMBER set MEMBER_AUTH = 1 where (select count(*) from MP_MEMBER_AUTH where MEMBER_EMAIL = #{memberEmail}) > 0]]>
</update>

 

메일인증을 하면 MEMBER_AUTH 칼럼을 기본값0에서 1로 바꿔, 로그인을 허용하는 로직으로 진행하려고 한다.

 

지난 글에서 아이디 중복검사를 했듯, 이메일도 중복검사를 할 수 있겠으나,

서비스용이 아닌 제작용인 커뮤니티에서 이메일 제한을 해버리면;

개발자인 내가 너무 힘들어지니 이 부분은 생략하겠다..

 


DAO

	public void createAuthKey(String memberEmail,String authKey) throws Exception;
		
	public void memberAuth(String memberEmail) throws Exception;

 

 DAOImlp

@Override
	public void createAuthKey(String memberEmail,String authKey) throws Exception{
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("memberEmail", memberEmail);
		map.put("authKey", authKey);
		
		sqlsession.selectOne("memberMapper.createAuthKey", map);
		
	}
	
	@Override
	public void memberAuth(String memberEmail) throws Exception{
		sqlsession.update("memberMapper.memberAuth", memberEmail);
	}

Service

	public MemberVO checkLoginBefore(String value) throws Exception;
		
	public void memberAuth(String memberEmail) throws Exception;

 

ServiceImpl

	@Inject
	private JavaMailSender mailSender;
	
	@Transactional
	@Override
	public void register(MemberVO memberVO) throws Exception{
		memberDAO.register(memberVO);
		
		String key = new TempKey().getKey(50,false);
		memberDAO.createAuthKey(memberVO.getMemberEmail(), key);
		MailUtils sendMail = new MailUtils(mailSender);
		sendMail.setSubject("[ICEWATER 커뮤니티 이메일 인증메일 입니다.]"); //메일제목
		sendMail.setText(
				"<h1>메일인증</h1>" +
						"<br/>"+memberVO.getMemberId()+"님 "+
						"<br/>ICEWATER에 회원가입해주셔서 감사합니다."+
						"<br/>아래 [이메일 인증 확인]을 눌러주세요."+
						"<a href='http://localhost:8080/member/registerEmail?memberEmail=" + memberVO.getMemberEmail() +
						"&key=" + key +
						"' target='_blenk'>이메일 인증 확인</a>");
		sendMail.setFrom("[발송 이메일 주소]", "[발송자 이름]");
		sendMail.setTo(memberVO.getMemberEmail());
		sendMail.send();
		
	}
    
    @Override
	public void memberAuth(String memberEmail) throws Exception{
		memberDAO.memberAuth(memberEmail);
	}

회원가입 서비스에 

인증키 생성로직과 메일발송 로직을 넣어줬다.

아까 작성한 TempKey와 MailUtils 클래스를 사용한다.

제목, 내용, 발송인, 작성자, send를 입력해준다.

 

회원가입 로직에 넣었기 때문에, 사용자가 입력한 항목을 모두 가져올 수 있다.

닉네임이나.. 전화번호나.. 생각한 형태로 VO에서 값을 가져와서 만들어주자.


Controller

 

	@RequestMapping(value = "/register", method=RequestMethod.POST)
	public String register(MemberVO memberVO, RedirectAttributes rttr, Model model)throws Exception{
		logger.info("register");
		String hashedPw = BCrypt.hashpw(memberVO.getMemberPw(), BCrypt.gensalt());
		memberVO.setMemberPw(hashedPw);
		memberService.register(memberVO);
		model.addAttribute("member", memberVO);
		
		
		rttr.addFlashAttribute("msg", "가입이 완료되었습니다");
		rttr.addAttribute("memberEmail", memberVO.getMemberEmail());
		rttr.addAttribute("memberId", memberVO.getMemberId());
		
		return "redirect:/member/registerAuth";
		
		
	}
	
	@RequestMapping(value="registerEmail", method=RequestMethod.GET)
	public String emailConfirm(String memberEmail,Model model)throws Exception{
		memberService.memberAuth(memberEmail);
		model.addAttribute("memberEmail", memberEmail);
		
		return "/member/registerEmail";
	}

 

회원가입과 이메일 인증 컨트롤러이다.

회원가입쪽은 memberVO(회원가입입력정보) 를 받아와서 서비스로 넘겨주는 부분이라

뷰단으로 리다이렉트 데이터를 주는 것 외엔 변한게 없고,

 

이메일인증 컨트롤러가 추가됐다.

 

로그인 컨트롤러

@RequestMapping(value="/login" , method= RequestMethod.POST)
	public String loginPost(LoginVO loginVO, HttpSession httpSession,Model model)throws Exception{
		logger.info("loginVO"+loginVO.getMemberId());
		MemberVO memberVO = memberService.login(loginVO);
		logger.info("Pw"+memberVO);
		if( memberVO == null || !BCrypt.checkpw(loginVO.getMemberPw(), memberVO.getMemberPw())) {
			return "/member/loginCheck";
		}
		
		if(memberVO.getMemberAuth() == 0) {
			model.addAttribute("Auth", memberVO.getMemberAuth());
			return "/member/registerReady";
		}
		
		
		model.addAttribute("member", memberVO);
		
		if(loginVO.isUseCookie()) {
			int amount = 60*60*24*7;
			Date sessionLimit = new Date(System.currentTimeMillis() + (1000*amount));
			memberService.keepLogin(memberVO.getMemberId(), httpSession.getId(), sessionLimit);
		
		}
		return "board/list";
		
		
	}

만약 이메일 인증을 받지 않았다면, 특정페이지로 가게 끔 컨트롤러에 

if(memberVO.getMemberAuth() == 0) {
model.addAttribute("Auth", memberVO.getMemberAuth());
return "/member/registerReady";
}

코드를 추가해준다


 

 이제 이메일 인증 기능을 구현했으니, 로직의 흐름대로 결과사진,뷰단 코드과 함께 설명해보려고 한다.

 

회원가입뷰

회원가입뷰에서 회원정보를 입력하고 가입을 누르면 /member/register 를 호출한다.

이제 해당 컨트롤러 부분을 보자.

입력한 memberVO가 서비스로 넘어가고 memberVO의 Email로 아까 셋팅한 이메일 인증 확인 메일이 날아간다.

 

 

registerAuth.jsp 생성

리다이렉트로 해당 페이지를 띄우고 , 로그인페이지에서 RedirectAttribute.addAttribute로 Email과 Id를 보내고,

RequestParam으로 받아서 입력받은 아이디와 이메일을 띄워준다.

 

@RequestMapping(value="/registerAuth",method= RequestMethod.GET)
	public String loginView(HttpServletRequest request,Model model,@RequestParam("memberEmail")String memberEmail,@RequestParam("memberId")String memberId) throws Exception{
		logger.info("loginView");
		
		model.addAttribute("memberEmail", memberEmail);
		model.addAttribute("memberId", memberId);
		
		
		return "/member/registerAuth";
	}

메일함에 가보니 메일이 잘 도착했다.


인증확인을 누르면

/member/registerEmail 을 호출한다.

이제 컨트롤러에서 get방식으로 전송된(주소창을 확인해보자) 이메일과 인증키를 받는다.

 

그럼 이제 Mapper.xml의 memberAuth 까지 올라가서

<update id="memberAuth">
	<![CDATA[update MP_MEMBER set MEMBER_AUTH = 1 where (select count(*) from MP_MEMBER_AUTH where MEMBER_EMAIL = #{memberEmail}) > 0]]>
</update>

 해당 로직을 실행한다.

 

그런데, 기능에는 별다른 문제가 없어서 암호키를 뺏고 이메일로만 인증을 했다.

보안을 높이기 위해 암호키까지 사용해서 인증을 하고싶다면 간단하다.

컨트롤러에서 매개변수로 String authKey 를 추가하고, 서브쿼리의 where문 뒤에 

AND MEMBER_AUTH = #{authKey} 를 추가해주면 되겠다.

 

DB에 저장된 이메일과, 링크로 전송된(GET) 이메일이 맞다면,

아까 추가한 회원테이블의 MEMBER_AUTH를 기본값인 0에서 1로 바꾼다.

 

로직을 마친후,  /member/registerEmail 뷰로 리턴해서 위의 사진의 내용을 띄운다.

확인버튼을 누르면 로그인페이지로 돌아간다.

 

registerEmail.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		var memberEmail = '${memberEmail}';

		alert(memberEmail + '님 회원가입을 축하합니다. 이제 로그인이 가능 합니다. 확인버튼을 누르면 로그인 페이지로 이동합니다.');

		self.location = "/member/loginView";
	</script>
</body>
</html>

만약 이메일 인증을 하지않고 (Member_AUTH가 0인상태로) 로그인을 하면

/member/registerReady로 이동한다.

 

 

위에서 올린 로그인 컨트롤러의 일부분

if(memberVO.getMemberAuth() == 0) {
			model.addAttribute("Auth", memberVO.getMemberAuth());
			return "/member/registerReady";
		}

 

메일전송 라이브러리와, 0과 1로 구분하는 AUTH의 사용방식에 대한 이해가 필요하다.

 

 

 

+++++++++

나중에 백퍼 안할 것 같아서 블로그 글 작성한 후, 바로 암호키도 사용해서 이메일인증을 하도록

만들었다.. 5분 걸렸나..? ㅜㅜ 귀찮아도 바로바로 해치워버리자.. 안좋은 습관.. 

DAOImpl의 memberAuth 
쿼리

 

728x90
저작자표시 (새창열림)

'SPRING > IceWater Community' 카테고리의 다른 글

[스프링]비밀번호 찾기 구현  (1) 2021.09.24
[스프링] 아이디찾기 구현  (1) 2021.09.24
[스프링] 자동로그인 기능 구현 - 로그인4  (0) 2021.09.17
[스프링]로그인 권한설정과(인터셉터) 로그인,로그아웃 전 페이지 기억 기능 - 로그인 3  (1) 2021.09.16
[스프링]인터셉터를 활용한 로그인구현 (세션부여) - 로그인2  (0) 2021.09.15
    'SPRING/IceWater Community' 카테고리의 다른 글
    • [스프링]비밀번호 찾기 구현
    • [스프링] 아이디찾기 구현
    • [스프링] 자동로그인 기능 구현 - 로그인4
    • [스프링]로그인 권한설정과(인터셉터) 로그인,로그아웃 전 페이지 기억 기능 - 로그인 3
    간펴니
    간펴니
    개발공부 기록하는 곳

    티스토리툴바