2024. 5. 23. 18:14ㆍ웹개발
회원가입 시 신원을 확인하기 위해 보편적으로 사용하는 전화번호 인증을 구현해 보았다.
참고:
CoolSms, Springboot, Redis 문자로 인증하기
하 일단 CoolSms, Springboot, Redis 이 세가지를 사용해서 문자 인증 구현하는 방법을 찾아봤는데 내용이 너무 별로 없어서 슬펐다 .. coolsms의 경우 몇년 전 글들이랑 지금 사용하는 방법이랑 차이가 있
velog.io
[Spring] 회원가입시 필요한 인증번호 관리
프로젝트의 전체 소스 코드는 이곳에서 확인하실 수 있습니다. 이전에 진행했었던 프로젝트에서도 휴대폰 인증을 통한 회원가입 인증번호를 구현했었는데 당시에는 인증번호의 일치 불일치 여
1-7171771.tistory.com
[Spring boot] 문자 SMS 인증 구현하기(2)
2021.12.22 - [Back-end/Spring & Spring Boot] - [Spring boot] 문자 SMS 인증 구현하기(1) 이전 포스팅에 SMS 문자로 인증 번호를 발송하는 부분을 구현해보았는데 이번 포스팅엔 발송했던 인증 번호를 저장해 두
diddl.tistory.com
[Spring] 문자 인증 구현하기 - coolSMS
항해 99 최종 프로젝트를 진행 중 Nice API와 같은 인증 서비스를 도입하고 싶었으나, 사업자 등록이 필요하다는 답변을 받고 차선책으로 이메일 찾기 기능의 인증 수단으로서 사용하였다.Old-Version
velog.io
※ 위의 블로그들을 정독하고 오시면 많은 도움이 됩니다. 😄😄
큰 틀로는 html에서 내 휴대전화를 폼으로 넘기면 서버에서 인증번호를 생성하고, 인증번호를 'redis'에 저장하고 'nurigo'라는 api를 사용해 내 휴대전화에 인증번호를 포함한 문자가 날라오는 형식이다. 그래서 휴대전화에 날라온 인증번호와 redis에 저장된 인증번호가 일치할 때에만 인증이 되는 형식이다.
<gradle>
//전화번호 인증 coolsms
implementation 'net.nurigo:sdk:4.3.0'
//redis
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '3.2.2'
<application.properties>
#전화번호 인증 coolSMS
spring.coolsms.apikey =
spring.coolsms.apisecret =
spring.coolsms.fromnumber =
spring.coolsms.provider = https://api.coolsms.co.kr
#redis
spring.redis.host=localhost
spring.redis.port=6379
spring.data.redis.repositories.enabled=false
<redis config>
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
// lettuce
@Bean
public RedisConnectionFactory redisConnectionFactory() { //redis 호스트, 포트 설정
return new LettuceConnectionFactory(redisHost, redisPort);
}
@Bean
public StringRedisTemplate stringRedisTemplate() { //redis에 문자열 데이터를 저장
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}
}
<UserCheckDTO>
import lombok.Getter;
import lombok.Setter;
public class UserCheckDTO {
@Getter
public static class SmsCertificationRequest {
private String phone;
private String certificationNumber;
}
}
<SmsCertification>
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import java.time.Duration;
@RequiredArgsConstructor
@Repository
public class SmsCertification {
private final String PREFIX = "sms:"; // key값이 중복되지 않도록 상수 선언
private final int LIMIT_TIME = 3*60; //인증번호 유효 시간
private final StringRedisTemplate stringRedisTemplate;
//Redis에 저장
public void createSmsCertification(String phone, String certificationNumber) {
stringRedisTemplate.opsForValue()
.set(PREFIX + phone, certificationNumber, Duration.ofSeconds(LIMIT_TIME));
}
//휴대전화 번호에 해당하는 인증번호 불러오기
public String getSmsCertification(String phone) {
return stringRedisTemplate.opsForValue().get(PREFIX + phone);
}
//인증 완료 시, 인증번호 Redis에서 삭제
public void deleteSmsCertification(String phone) {
stringRedisTemplate.delete(PREFIX + phone);
}
//Redis에 해당 휴대번호로 저장된 인증번호가 존재하는지 확인
public boolean hasKey(String phone) {
return stringRedisTemplate.hasKey(PREFIX + phone);
}
}
<SmsUtil>
import net.nurigo.sdk.NurigoApp;
import net.nurigo.sdk.message.request.SingleMessageSendingRequest;
import net.nurigo.sdk.message.response.SingleMessageSentResponse;
import net.nurigo.sdk.message.service.DefaultMessageService;
import net.nurigo.sdk.message.model.Message;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class SmsUtil {
@Value("${spring.coolsms.fromnumber}")
private String fromNumber;
@Value("${spring.coolsms.apikey}")
private String apiKey;
@Value("${spring.coolsms.apisecret}")
private String apiSecretKey;
@Value("${spring.coolsms.provider}")
private String provider;
private DefaultMessageService messageService;
@PostConstruct
private void init() {
this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecretKey, provider);
}
//단일 메시지 발송 예제
public SingleMessageSentResponse sendSMS(String to, String verificationCode) {
Message message = new Message();
//발신번호 및 수신번호는 반드시 01012345678 형태로 입력되어야 함!!
message.setFrom(fromNumber);
message.setTo(to);
message.setText("[모해먹] 인증번호를 입력해 주세요\n" + verificationCode);
try {
SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message));
System.out.println(response);
return response;
} catch (Exception e) {
System.out.println("Exception while sending SMS: " + e.getMessage());
e.printStackTrace(); // 스택 트레이스 출력
throw e; // 예외를 다시 던져서 상위 메서드로 전파
}
}
}
<UserCheck>
import com.example.enlaco.DTO.UserCheckDTO;
public interface UserCheck {
void sendSMS(UserCheckDTO.SmsCertificationRequest requestDTO);
void verifySMS(UserCheckDTO.SmsCertificationRequest requestDTO);
boolean isVerify(UserCheckDTO.SmsCertificationRequest requestDTO);
}
<UserCheckService>
import com.example.enlaco.DTO.UserCheckDTO;
import com.example.enlaco.Repository.SmsCertification;
import com.example.enlaco.Util.SmsUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.enlaco.Exceptions.CustomExceptions;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserCheckService implements UserCheck{
private final SmsUtil smsUtil;
private final SmsCertification smsCertification;
public void sendSMS(UserCheckDTO.SmsCertificationRequest requestDTO) {
// 요청 DTO에서 전화번호를 가져옵니다.
String to = requestDTO.getPhone();
// 1000에서 9999 사이의 랜덤한 인증번호를 생성합니다.
int randomNumber = (int) (Math.random() * 9000) + 1000;
String certificationNumber = String.valueOf(randomNumber);
System.out.println("Sending SMS to: " + to + " with code: " + certificationNumber);
// SMS를 발송합니다.
smsUtil.sendSMS(to, certificationNumber);
// 생성한 인증번호를 저장합니다.
smsCertification.createSmsCertification(to, certificationNumber);
}
public boolean isVerify(UserCheckDTO.SmsCertificationRequest requestDTO) {
String formCertificationNumber = requestDTO.getCertificationNumber();
//redis 인증번호와 폼에서 보낸 인증번호가 일치할 때 true, redis 데이터 삭제
if (formCertificationNumber.equals(smsCertification.getSmsCertification(requestDTO.getPhone()))) {
smsCertification.deleteSmsCertification(requestDTO.getPhone());
return true;
}
return false;
}
}
<SmsCertificationController>
import com.example.enlaco.DTO.UserCheckDTO;
import com.example.enlaco.Exceptions.ResponseMessage;
import com.example.enlaco.Exceptions.StatusCode;
import com.example.enlaco.Service.UserCheck;
import com.example.enlaco.Service.UserCheckService;
import com.example.enlaco.Util.DefaultRes;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.enlaco.Exceptions.CustomExceptions;
@RestController
@RequiredArgsConstructor
@RequestMapping("/sms-certification")
public class SmsCertificationController extends BaseController{
private final UserCheckService userCheckService;
private final UserCheck userCheck;
//전송
@PostMapping("/send")
public ResponseEntity<?> sendSms(@RequestBody UserCheckDTO.SmsCertificationRequest requestDTO) throws Exception {
System.out.println("Received phone number: " + requestDTO.getPhone() + requestDTO.getCertificationNumber());
try {
userCheck.sendSMS(requestDTO);
return new ResponseEntity(DefaultRes.res(StatusCode.OK, ResponseMessage.SMS_CERT_MESSAGE_SUCCESS), HttpStatus.OK);
} catch (CustomExceptions.Exception e) {
logger.error("Error occurred while sending SMS: ", e);
return handleApiException(e, HttpStatus.BAD_REQUEST);
}
}
//인증번호 확인
@PostMapping("/confirm")
public ResponseEntity<Void> SmsVerification(@RequestBody UserCheckDTO.SmsCertificationRequest requestDTO) throws Exception {
try {
if (userCheck.isVerify(requestDTO)){
return new ResponseEntity(DefaultRes.res(StatusCode.OK, ResponseMessage.SMS_CERT_SUCCESS), HttpStatus.OK);
}else {
// 인증이 실패한 경우 실패 응답을 반환
return new ResponseEntity(DefaultRes.res(StatusCode.BAD_REQUEST, ResponseMessage.SMS_CERT_FAILED), HttpStatus.BAD_REQUEST);
}
} catch (CustomExceptions.Exception e) {
return handleApiException(e, HttpStatus.BAD_REQUEST);
}
}
}
이어서 인증을 해보겠습니다.
회원가입 번호인증(2)
직접 인증을 해봤습니다. async function sendSMS() { //사용자로부터 번호를 입력 받는 요소 const phone = document.getElementById("mphone").value; //서버로 보낼 데이터 const data = { phone: phone }; //Fetch API를 사용해 a
studydogyu.tistory.com
※ 피드백 및 지적 감사합니다.
'웹개발' 카테고리의 다른 글
스프링부트) 회원가입 네이버 이메일 인증 2 (0) | 2024.05.29 |
---|---|
스프링부트) 회원가입 네이버 이메일 인증 1 (0) | 2024.05.29 |
스프링부트) 회원가입 번호인증(2) (0) | 2024.05.23 |
스프링부트) 회원가입 시 우편번호 검색 (0) | 2024.05.13 |
스프링부트 ajax를 사용한 '좋아요' 비동기 처리 (0) | 2024.05.11 |