티스토리 뷰
현재 개발 중인 서비스에서 본인 인증 기능이 필요해졌다.
한번쯤 써봤을 PASS 본인인증 같은 기능이다.
내가 인증 대상이 될 때는 몰랐는데, 본인 인증 기능을 개발하려니 업체 선정에서부터 어려운 점이 생겼다.
결국은 요금이 가장 큰 걸림돌되었고,(현재 서비스는 사용량이 썩 많지 않다.)
우선 대표적인 국내 업체 두 곳을 비교했다.
구글링을 하면 가장 먼저 나오는 곳은 PASS와 드림시큐리티였다.
하지만 두 업체는 가장 먼저 후보군에서 제외 됐다.
두 곳 모두, 기본료가 청구된다는 것이다.
서비스 이용자가 많지 않아서 기본료가 청구되는 상황은 배보다 배꼽이 크다 판단했다.
또 SDK에 대한 정보가 하나도 없어서 구현 난이도나 주고 받는 정보가 우리 서비스에 맞을지 가늠이 잘 안됐다.
(인증 UI까지 제공하기 때문에 그런 것 같기도 하고)
결국 다른 서비스로 눈을 돌렸고 눈에 들어온게 해외 서비스인 Twilio다.
일단 Twilio는 해외에서 제공하는 Email, SMS, 전화통화를 이용한 인증 서비스를 제공하는 업체다.
(무려 이메일 서비스를 주로 하는 sendgrid의 모회사! 다)
우선 가격을 보면,
가격은 약 0.05$로 한화로 치면 건당 66.9원으로 다른 곳보다 상대적으로 비싸다.
그러나, 별도의 기본료나 계약이 필요 없다는 점과
기본적으로 $15.5 를 포인트로 제공해서 프리티어 기능을 이것저것 써볼 수 있다는 장점이 있다.
서비스 설정하기
일단 이메일로 회원가입을 하고 나면 메인 화면 아래와 같이 나온다.
휠을 내리면 Account SID와 Auth Token이 나오는데 잘 복사해두자.
서비스 탭을 누르고, Create new로 내가 만들 앱의 이름을 적어 준다.
난 SMS만 쓸꺼니까 SMS만 체크 후 이름을 지정
그러면 앱이 생성되고 Service SID가 나온다. 이것도 잘 복사해두자.
앱 이름을 클릭해서 들어가면 Service settings가 나오는데, SMS 인증 코드의 길이를 설정할 수 있다.
휠을 내려보면 메시지 템플릿을 설정할 수 있는데, 프리티어 사용자는 서비스의 이름이 아닌 [SAMPLE TEST]가 출력된다. 기본 템플릿으로 몇 가지가 제공되기도 한다.
여기까지 Twilio의 Service 페이지에서 설정할 수 내용들이고 스프링에서 구현해보자.
구현
build.gradle
Twilio는 E164라는 번호 포맷을 사용한다. 구글에서 제공하는 라이브러리를 사용해 변환했다.
implementation 'com.twilio.sdk:twilio:9.13.1'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.30'
Configuration
application.yml에 있는 값들을 가져왔음
@Configuration
public class TwilioConfig {
@Value("${twilio.account-sid}")
private String accountSid;
@Value("${twilio.auth-token}")
private String authToken;
@PostConstruct
public void init() {
Twilio.init(accountSid, authToken);
}
}
Model
요청 객체는 별게 없다.
public class UserVerifyCodeRequestDto {
private String phone;
}
public class UserVerifyCheckRequestDto {
private String phone;
private String code;
}
Controller
인증 코드 발급 API와 인증 코드 검증 API
@GetMapping("/user/authenticate/code")
public ResponseEntity<Object> getUserAuthenticateCode(UserVerifyCodeRequestDto userVerifyCodeRequestDto) {
return userAuthenticateService.startVerification(userVerifyCodeRequestDto);
}
@PostMapping("/user/authenticate")
public ResponseEntity<Object> verifyUserAuthenticateCode(@RequestBody UserVerifyCheckRequestDto userVerifyCheckRequestDto) {
return userAuthenticateService.checkVerification(userVerifyCheckRequestDto);
}
Service
E.164 번호 변환은 여기에서 확인하고, 번호 변환 로직은 공통클래스로 빼줬다.
// 인증 번호 요청
public ResponseEntity<Object> verification(UserVerifyCodeRequestDto userVerifyCodeRequestDto) {
String e164FormatPhoneNumber = CommonUtils.getE164FormatPhoneNumber(userVerifyCodeRequestDto.getPhone());
Verification verification = Verification.creator(
serviceSid, // this is your verification sid
e164FormatPhoneNumber, //this is your Twilio verified recipient phone number
"sms") // this is your channel type
.create();
System.out.println(verification.getStatus());
return new ResponseEntity<>("인증 번호 요청 성공", HttpStatus.OK);
}
// 인증 번호 검증
public ResponseEntity<Object> verificationCheck(UserVerifyCheckRequestDto userVerifyCheckRequestDto) {
String e164FormatPhoneNumber = CommonUtils.getE164FormatPhoneNumber(userVerifyCheckRequestDto.getPhone());
try {
VerificationCheck verificationCheck = VerificationCheck.creator(
serviceSid)
.setTo(e164FormatPhoneNumber)
.setCode(userVerifyCheckRequestDto.getCode())
.create();
System.out.println(verificationCheck.getStatus());
} catch (Exception e) {
return new ResponseEntity<>("인증 번호 검증 실패", HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("인증 번호 검증 성공", HttpStatus.OK);
}
간단한 구현은 여기까진데, 눈에 띄는 문제가 하나 있다.
인증 번호 요청이 무한 반복 가능하다는 점이다.
Twilio는 10분 간은 같은 인증 번호를 내려준다.
(이 interval을 조정하려면 본인들 서포트 센터에 연락을 주라고 한다 https://www.twilio.com/docs/verify/api/rate-limits-and-timeouts )
이에 대한 처리 방법은 다음 글에서 정리해보겠다.
마치며
위에서 언급한 같은 사용자의 반복적인 요청 외에도, 고려해야하는 사항들이 더 있다.
비용이 건당 약 70원이라는건 가벼워보이지만, 사용자가 악의적으로 공격이 들어오면 비용이 줄줄 새게 될 것이다.
이걸 막기위해서 몇 가지를 고려했는데 이건 다음 포스팅에서 알아보자.
참고자료
https://www.twilio.com/blog/phone-number-verification-java-spring-boot-verify-totp
'개발 > SPRING' 카테고리의 다른 글
스프링부트에서 kakao oauth 2.0 로그인 구현하기 - CSR(클라이언트 사이드 랜더링) 기반 (0) | 2023.11.02 |
---|---|
스프링부트에서 SMS 본인 인증 구현하기 - 2. 어뷰징 막기 (0) | 2023.10.30 |
스프링부트에서 JWT 적용하기 - 3. 필터(filter)에 적용 (0) | 2023.10.10 |
스프링부트에서 JWT 적용하기 - 2. 인터셉터(Interceptor)에 적용 (1) | 2023.10.06 |
스프링부트에서 JWT 적용하기 - 1. JWT란? (0) | 2023.10.05 |
- Total
- Today
- Yesterday
- 티스토리챌린지
- terraform
- CloudFront
- AWS EC2
- elasticsearch
- AOP
- springboot
- OpenAI
- AWS
- ChatGPT
- 후쿠오카
- GIT
- cache
- 오블완
- Elastic cloud
- java
- S3
- MySQL
- JWT
- OpenFeign
- 람다
- 스프링부트
- EKS
- Kotlin
- openAI API
- serverless
- Spring
- docker
- lambda
- Log
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |