Notice
Recent Posts
Recent Comments
Link
«   2023/12   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

Been_DevStep

SpringBoot 이메일 인증(1) 본문

공부/SpringBoot

SpringBoot 이메일 인증(1)

JChBeen 2022. 11. 2. 14:21
1 받은 이메일을 사용하는 유저가 레코드가 잇는가? -> 이미 사용중인 이메일이 주소입니다.
2 (Apache Commons Langs 사용하여)인증번호 및 솔트(Salt)생성 후 테이블에 인서트
3 <2>에서 생성된 인증번호 전송 -> " 인증번호를 전송하였습니다. 5분이내에 입력해 주세요."

이 모든 동작은 transaction(트랜잭션)이 적용되어야한다.

 

Controller

//이메일 인증 요청을 위한 인증번호를 전송하기위한 Controller
@RequestMapping(value = "email", method = RequestMethod.POST,
        produces = MediaType.APPLICATION_JSON_VALUE)  //value 는 js와 연결
@ResponseBody//String  결과값이 타임리프 문이 아니라 그냥 결과값이 출력됨
public String postEmail(UserEntity user, EmailAuthEntity emailAuth) throws  MessagingException {
    Enum<?> result = this.memberService.sendEmailAuth(user, emailAuth);
    JSONObject responseObject = new JSONObject();
    responseObject.put("result", result.name().toLowerCase());
    
    if (result == CommonResult.SUCCESS)
        responseObject.put("salt", emailAuth.getSalt());
    //실패라면 굳이 데이터를 추가로 보낼 필요가 없기때문에 성공일 경우 데이터를 담아보냅니다.
    //Salt는 추후 회원가입할때 확인하기 위해서 데이터를 담아서 보내줍니다.
    //이 값은 hidden 으로 보관할 것 입니다.

    return responseObject.toString();
    //toLowerCase를 사용하는 이유는 JAVA로 개발했다는 것을 숨기기 위해서
}

Service

@Transactional  // 트랜잭션 설정.
public Enum< ? extends IResult> sendEmailAuth(UserEntity user, EmailAuthEntity emailAuth) {
        UserEntity existingUser = this.memberMapper.selectUserByEmail(user.getEmail());

        // null 이 아니라면 이미 동일한 email 로 가입이 되어있다는 의미이기
        // 때문에 중복 결과를 return 합니다.
        if(existingUser != null) return CommonResult.FAILURE;

Service에서 결과값을 Enum을 반환해줍니다.

 

SendEmailAuthResult

//서비스이름.결과값
public enum SendEmailAuthResult implements IResult {
    EMAIL_DUPLICATED
}

 

 

 

IMemberMapper Interface

UserEntity selectUserByEmail(@Param(value = "email") String email);

MemberMapper.xml

<select id="selectUserByEmail"
        resultType="dev.test.study_member_bbs.entities.member.UserEntity">
    SELECT `email`             AS `email`,
           `password`          AS `password`,
           `nickname`          AS `nickname`,
           `name`              AS `name`,
           `contact`           AS `contact`,
           `address_postal`    AS `addressPostal`,
           `address_primary`   AS `addressPrimary`,
           `address_secondary` AS `addressSecondary`,
           `registered_on`     As `registeredOn`
    FROM `study_member`.`user`
    WHERE BINARY `email` = #{email}
        LIMIT 1
</select>

 

Service에서 결과 값이 null이 아니라고 판명 되었다는 것은 중복체크가 끝났다는 의미입니다.

중복 체크가 끝났다면 emailAuth에 담을 데이터를 만들어주고 email_auth table에 담아줍니다.

//emailAuth 에 담을 email set
emailAuth.setEmail(user.getEmail());

//random code 6자리 set
//RandomStringUtils.randomNumeric(count) 는
//count 만큼의 숫자를 랜덤으로 생성한뒤 문자열로 반환해준다.
String code = RandomStringUtils.randomNumeric(6);
emailAuth.setCode(code);

// 랜덤한 salt 생성을 Math.random 을 사용한다.
// Math.random 는 0... ~ 0.9...까지의 값을 반환 한다.
String salt = String.format("%s%s%s%s", user.getEmail(), code, Math.random(), Math.random());
//생선한 salt 는 hashing 해서 set
emailAuth.setSalt(CryptoUtils.hashSha512(salt));

// slat 생성 시간.
Date createdOn = new Date();
emailAuth.setCreatedOn(createdOn);

// slat 생성 시간에서 5분을 더한 만료시간.
emailAuth.setExpiresOn(DateUtils.addMinutes(createdOn, 5));

//아직 인증을 완료하지 않았기 때문에 기본값인 false set
emailAuth.setExpired(false);

// 만들어준 emailAuth 를 db에 담아줍니다.
// insertEmailAuth 의 return 값은 int 인데 이 때 int 는
// insertEmailAuth 작업을 통해 영향을 끼친 레코드의 갯수를 의미합니다.
// 값이 0 이라면 insert 가 안되었다는 의미 이기 때문에 FAILURE 
if (memberMapper.insertEmailAuth(emailAuth) == 0)
    return CommonResult.FAILURE;
    
return CommonResult.SUCCESS;
}

IMemberMapper Interface

int insertEmailAuth(EmailAuthEntity emailAuth);

MemberMapper.xml

<insert id="insertEmailAuth"
            useGeneratedKeys="true"
            keyProperty="index"
            keyColumn="index"
            parameterType="dev.test.study_member_bbs.entities.member.UserEntity">
        INSERT INTO `study_member`.`email_auths` (`email`, `code`, `salt`, `created_on`, `expires_on`, `expired_flag`)
        VALUES (#{email}, #{code}, #{salt}, #{createdOn}, #{expiresOn}, #{isExpired})
    </insert>

Table에 PK가 index일 경우 useGeneratedKeys, keyProperty, keyColumn을 걸어줍니다.

 

 

register.js

const xhr = new XMLHttpRequest();

const formData = new FormData();
formData.append('email', form['email'].value);
                    // (1.해당 Controller의 ReuestParam or
                    //매개변수에 있는 멤버변수의 이름과 동일하게사용.
                // 2.전송하고자하는 데이터가 속해있는 태그의 name )
xhr.open('POST', './email');
xhr.onreadystatechange = () => {
    if (xhr.readyState == XMLHttpRequest.DONE) {
        Cover.hide();
        if (xhr.status >= 200 && xhr.status < 300) {
                //status 가 200~ 299 까지는 성공 300~500 모종의 이유로 실패
                //http error code  참고
            console.log(xhr.responseText);
        } else {
            form.querySelector('[rel="emailWarning"]').innerText = '서버와 통신하지 못하였습니다. 잠시후 다시 시도해주세요.';
            form.querySelector('[rel="emailWarning"]').classList.add('visible');
        }
    }
};
xhr.send(formData);

를통해서

 

해당 이미지에 Response의  'success' 는 Controller의 retrun 값이고

Console은  js의 출력물 입니다.

Comments