본문 바로가기
Web

웹프로젝트 문자인증 구현하기 with 네이버 클라우드 플랫폼

by LasBe 2022. 5. 30.
반응형

⚡️Naver Cloud Flatform SENS API를 통한 문자인증


 

이번 JAVA - Spring 숙박 플랫폼 프로젝트의 회원가입 기능에 휴대전화를 통한 문자인증 기능을 구현했습니다.

 

인증문자를 휴대폰으로 전송하는 기능은 네이버 클라우드 플랫폼의 SENS를 이용하였습니다.

 

그럼 기능 구현을 위한 단계를 차례대로 소개합니다.

 

 

 

⚡️Naver Cloud Flatform 세팅


 

 

NAVER CLOUD PLATFORM

cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification

www.ncloud.com

  • 우선 위 링크로 이동해 회원가입을 진행해줍니다.

📌 인증키 생성

  • 마이페이지 -> 인증키 관리로 들어가줍니다.
  • 신규 API 인증키 생성을 클릭해 인증키를 생성해줍니다.


📌 SENS 프로젝트 생성

  • 우상단 콘솔을 클릭합니다.
  • Service 메뉴에서 SENS를 검색해주고 프로젝트를 생성합니다.
  • 열쇠 모양을 클릭해 서비스 ID를 확인할 수 있습니다.


📌 발신번호 등록

메세지를 전송하기 위해서는 인증된 전화번호를 등록해야합니다.

저는 개인적인 프로젝트었기 때문에 제 번호를 발신번호로 등록해 사용했습니다.

 

  • 좌측 사이드바 -> SENS -> SMS -> Calling Number
  • 프로젝트 선택 -> 발신번호 등록 탭 선택
  • 본인 전화번호 인증 후 등록


📌 API 호출 코드

저는 아래 코드를 util 패키지에 별도로 만들어두었습니다.

메세지를 보내는 메소드는 전화번호와 난수를 입력받도록 되어있습니다.

 

API를 이용하기 위해서는 위에서 미리 준비해두었던 인증키와 Service ID, 발신번호 등을 입력하면 됩니다.

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class Naver_Sens_V2 {
	@SuppressWarnings("unchecked")
	public void send_msg(String tel, String rand) {
        // 호스트 URL
        String hostNameUrl = "https://sens.apigw.ntruss.com";
        // 요청 URL
        String requestUrl= "/sms/v2/services/";
        // 요청 URL Type
        String requestUrlType = "/messages";
        // 개인 인증키
        String accessKey = "";
        // 2차 인증을 위해 서비스마다 할당되는 service secret
        String secretKey = "";
        // 프로젝트에 할당된 SMS 서비스 ID
        String serviceId = "";
        // 요청 method
        String method = "POST";
        // current timestamp (epoch)
        String timestamp = Long.toString(System.currentTimeMillis());
        requestUrl += serviceId + requestUrlType;
        String apiUrl = hostNameUrl + requestUrl;

        // JSON 을 활용한 body data 생성
        JSONObject bodyJson = new JSONObject();
        JSONObject toJson = new JSONObject();
        JSONArray  toArr = new JSONArray();
        
        // 난수와 함께 전송
        toJson.put("content","Going 본인인증 ["+rand+"]");		
        toJson.put("to",tel);
        toArr.add(toJson);
	    
        // 메시지 Type (sms | lms)
        bodyJson.put("type","sms");
        bodyJson.put("contentType","COMM");
        bodyJson.put("countryCode","82");
        
        // 발신번호 * 사전에 인증/등록된 번호만 사용할 수 있습니다.
        bodyJson.put("from","");		
        bodyJson.put("messages", toArr);		
	    
        String body = bodyJson.toJSONString();
	    
        System.out.println(body);
	    
        try {
            URL url = new URL(apiUrl);

            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setUseCaches(false);
            con.setDoOutput(true);
            con.setDoInput(true);
            con.setRequestProperty("content-type", "application/json");
            con.setRequestProperty("x-ncp-apigw-timestamp", timestamp);
            con.setRequestProperty("x-ncp-iam-access-key", accessKey);
            con.setRequestProperty("x-ncp-apigw-signature-v2", makeSignature(requestUrl, timestamp, method, accessKey, secretKey));
            con.setRequestMethod(method);
            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();
            
            System.out.println(response.toString());

        } catch (Exception e) {
            System.out.println(e);
        }
    }
	
    public static String makeSignature(
        String url, 
        String timestamp, 
        String method, 
        String accessKey, 
        String secretKey
    ) throws NoSuchAlgorithmException, InvalidKeyException {
        
	    String space = " "; 
	    String newLine = "\n"; 
	    
	    String message = new StringBuilder()
	        .append(method)
	        .append(space)
	        .append(url)
	        .append(newLine)
	        .append(timestamp)
	        .append(newLine)
	        .append(accessKey)
	        .toString();

	    SecretKeySpec signingKey;
	    String encodeBase64String;
		try {
			signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
			Mac mac = Mac.getInstance("HmacSHA256");
			mac.init(signingKey);
			byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
			encodeBase64String = Base64.getEncoder().encodeToString(rawHmac);
		} catch (UnsupportedEncodingException e) {
			encodeBase64String = e.toString();
		}
	    

	  return encodeBase64String;
	}
}

📌 난수 생성

난수를 생성하는 부분은 서비스단에서 처리하도록 하였습니다.

 

전화번호를 입력 받고, 생성한 난수를 반환합니다.

public String sendRandomMessage(String tel) {
    Naver_Sens_V2 message = new Naver_Sens_V2();
    Random rand = new Random();
    String numStr = "";
    for (int i = 0; i < 6; i++) {
        String ran = Integer.toString(rand.nextInt(10));
        numStr += ran;
    }
    System.out.println("회원가입 문자 인증 => " + numStr);

    message.send_msg(tel, numStr);

    return numStr;
}

📌 Controller

프론트에서 AJAX를 이용해 비동기적으로 구현했습니다.

 

전화번호를 받을 때 전화번호 중복체크까지 함께할 수 있도록 구현하였고,

보안을 위해 세션으로 코드를 체크하도록 했는데 정답인지는 모르니 직접 수정해서 사용하면 됩니다.

@PostMapping("phoneAuth")
@ResponseBody
public Boolean phoneAuth(String tel) {

    try { // 이미 가입된 전화번호가 있으면
        if(memberService.memberTelCount(tel) > 0) 
            return true; 
    } catch (Exception e) {
        e.printStackTrace();
    }

    String code = memberService.sendRandomMessage(tel);
    session.setAttribute("rand", code);
    
    return false;
}

@PostMapping("phoneAuthOk")
@ResponseBody
public Boolean phoneAuthOk() {
    String rand = (String) session.getAttribute("rand");
    String code = (String) request.getParameter("code");

    System.out.println(rand + " : " + code);

    if (rand.equals(code)) {
        session.removeAttribute("rand");
        return false;
    } 

    return true;
}

 

 

SENS 개요 - Simple & Easy Notification Service

 

api.ncloud-docs.com

자세한 API 사용법은 위 가이드를 참고해주시면 됩니다.

 

자바 스프링 java spring 네이버 클라우드 플랫폼 naver cloud flatform SENS 문자인증 문자전송 본인인증구현 문자인증구현 

 

반응형

댓글


오픈 채팅