1. 카카오 소셜 로그인 API
서버에서 클라이언트에게 액세스 토큰을 발급받아서 카카오로부터 사용자에 대한 정보를 얻어오고 해당 이메일이 서비스에 가입되어 있는지 검사를 하였다.
[일반 회원가입 - 해당 이메일로 가입한 유저가 있는지 검사]
public PostUserRes createUser(PostUserReq postUserReq) throws BaseException {
// 가입 중복 이메일 검증
if(userProvider.checkEmail(postUserReq.getEmail()) == 1) {
throw new BaseException(DUPLICATED_EMAIL);
}
if(userProvider.checkPhone(postUserReq.getPhone()) == 1) {
throw new BaseException(DUPLICATED_PHONE);
}
String pwd;
try{
//비밀번호 암호화
pwd = new AES128(Secret.USER_INFO_PASSWORD_KEY).encrypt(postUserReq.getPassword());
postUserReq.setPassword(pwd);
} catch (Exception ignored) {
throw new BaseException(PASSWORD_ENCRYPTION_ERROR);
}
try{
int userIdx = userDao.createUser(postUserReq, "INAPP"); // 가입유형 저장
return new PostUserRes(userIdx);
} catch (Exception exception) {
throw new BaseException(DATABASE_ERROR);
}
}
해당 서비스에 가입하지않았는데 다른사람이 내 이메일로 가입한 경우가 있을 수 있다.
이런 상황에서 사실상 다른 사람의 계정인데 이메일이 같다고 카카오 회원에게 jwt토큰을 줄 수 없다고 생각해서 User 테이블에 joinType이라는 컬럼을 추가하였다. 그리고 컬럼값은 [INAPP, KAKAO] 두 가지로 나누어 구분해주었다.
이걸로 이미 존재하는 이메일이 인앱가입인지, 카카오가입인지 구분해서 카카오 로그인으로 이어주거나 서비스 이메일로 로그인하라는 메시지를 전달해줄 수 있었다.
결론적으로 카카오 이메일이 INAPP타입으로 가입된 상태라면 서비스로그인, 가입되어있지않은 이메일인 경우에는 카카오에서 받은 이메일, 닉네임을 얻어와서 회원가입처리한 후 카카오 로그인으로 진행한 후 jwt 응답을 통해 진행을 이어주었다.
<<카카오 로그인 Controller와 Service단 코드>>
/**
* 3. 카카오 로그인 API
* [POST] /users/login/kakao
*/
@ResponseBody
@PostMapping("/login/kakao")
public BaseResponse<PostLoginRes> kakaoLogin(@RequestBody PostKakaoLoginReq postKakaoLogin) {
if (postKakaoLogin.getAccessToken() == null || postKakaoLogin.getAccessToken().isEmpty()) {
return new BaseResponse<>(AUTH_KAKAO_EMPTY_TOKEN);
}
try {
// 액세스 토큰으로 사용자 정보 받아온다.
KaKaoUserInfo kaKaoUserInfo = KakaoApiService.getKakaoUserInfo(postKakaoLogin.getAccessToken());
// 로그인 처리 or 회원가입 진행 후 jwt, userIdx 반환
PostLoginRes postLoginRes = userProvider.kakaoLogin(kaKaoUserInfo);
return new BaseResponse<>(postLoginRes);
} catch (BaseException exception) {
logger.warn("#3. " + exception.getStatus().getMessage());
logger.warn(postKakaoLogin.toString());
return new BaseResponse<>(exception.getStatus());
}
}
2. 네이버 SENS SMS API
네이버에서 제공하는 심플이지노티피케이션 서비스 API를 이용해서 SMS인증번호 발송 기능을 구현하였다.
이 기능은 회원가입 시, 휴대폰번호를 입력했는데 이미 다른 사람이 가입한 번호라면 자기 번호라고 인증하는 용도와 아이디찾기, 비밀번호 찾기에서 필요한 기능이어서 개발하였다.
@Service
public class SmsAuthService {
private final UserDao userDao;
@Autowired
public SmsAuthService(UserDao userDao) {
this.userDao = userDao;
}
public PhoneAuthInfo checkExistUserPhoneAndSendAuth(PostFindEmailAuthReq postFindEmailAuthReq) throws BaseException {
if (userDao.checkUserByNameAndPhone(postFindEmailAuthReq.getUserName(), postFindEmailAuthReq.getPhone()) == 1) {
return sendPhoneAuth(postFindEmailAuthReq.getPhone()); // 해당 이름과 전화번호로 가입된 회원이 있다면 인증번호를 발송한다.
} else {
throw new BaseException(USERS_NOT_FOUND_INFO); // 입력된 이름과 전화번호로 가입된 회원이 없음
}
}
public PhoneAuthInfo sendPhoneAuth(String toPhone) throws BaseException {
int authNumber = (int) (Math.random() * (99999 - 10000 + 1)) + 10000; // 인증번호 난수 생성
String accessKey = SENS_ACCESS_KEY;
String serviceId = SENS_SERVICE_ID;
String method = "POST";
String timestamp = Long.toString(System.currentTimeMillis());
String tophone = toPhone;
String requestURL = "https://sens.apigw.ntruss.com/sms/v2/services/" + serviceId + "/messages";
//JSON을 활용한 doby data 생성
JSONObject bodyJson = new JSONObject();
JSONObject toJson = new JSONObject();
JSONArray toArr = new JSONArray();
toJson.put("to", tophone); // 메시지 수신자
toArr.add(toJson);
bodyJson.put("type", "sms");
bodyJson.put("contentType", "comm");
bodyJson.put("countryCode", "82");
bodyJson.put("from", SENDERNUMBER);
bodyJson.put("content", "[13th-쿠팡이츠] 인증번호\n" + authNumber);
bodyJson.put("messages", toArr);
String body = bodyJson.toJSONString();
//System.out.println("body: : " + body);
try {
URL url = new URL(requestURL);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setUseCaches(false);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestProperty("content-type", "application/json; charset=utf-8");
con.setRequestProperty("x-ncp-apigw-timestamp", timestamp);
con.setRequestProperty("x-ncp-iam-access-key", accessKey);
con.setRequestProperty("x-ncp-apigw-signature-v2", makeSignature(timestamp));
con.setRequestMethod(method); // POST 방식
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.write(body.getBytes());
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
BufferedReader br;
//System.out.println("responseCode : " + responseCode);
if (responseCode == 202) { //정상
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
}
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
response.append(inputLine);
}
br.close();
con.disconnect();
if (responseCode != 202) { // 정상이 아니라면
throw new BaseException(FAILED_TO_SEND_PHONE_AUTH);
}
return new PhoneAuthInfo(tophone, String.valueOf(authNumber));
} catch (Exception ignored) {
throw new BaseException(FAILED_TO_SEND_PHONE_AUTH);
}
}
private static String makeSignature(String timestamp) throws BaseException {
String encodeBase64String = "";
String space = " "; // one space
String newLine = "\n"; // new line
String method = "POST";
String url = String.format("/sms/v2/services/%s/messages", SENS_SERVICE_ID);
String message = new StringBuilder().append(method).append(space).append(url).append(newLine)
.append(timestamp).append(newLine).append(SENS_ACCESS_KEY).toString();
try {
SecretKeySpec signingKey = new SecretKeySpec(SENS_SECRET_KEY.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
encodeBase64String = java.util.Base64.getEncoder().encodeToString(rawHmac);
return encodeBase64String;
} catch (Exception ignored) {
throw new BaseException(FAILED_TO_SEND_PHONE_AUTH);
}
}
}
'프로젝트 > 쿠팡이츠 클론코딩' 카테고리의 다른 글
[쿠팡이츠 클론코딩] ERD 설계 (0) | 2022.03.11 |
---|---|
[쿠팡이츠 클론코딩] 개발 범위 및 API 명세서 (0) | 2022.03.11 |
댓글