[naver] 네이버웍스 API 2.0 토큰인증 : 메시지발송 예제

2023. 12. 22. 14:21naver

token jwt 계정 인증

 

네이버웍스 2.0 API를 사용하기 위해서는 인증먼저 해야한다. 인증 방법에는 Oauth방식과 JWT 방식이 있다.

Spring 기반의 예제를 작성해보도록 하겠다.

 

1. Developer Console 사이트접속후 각종 인증키 확보

- Client ID

- Client Secret

- Service Account
- Private Key

naver works api key
naver works api key

 

2. 스프링 pom.xml

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.0</version>
</dependency>

 

3. RSAUtils.java

rsa를 사용해야 하기때문에 Util파일로 하나 생성한다.

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;

public class RSAUtils {
	
	public static String getKey(String filename) throws IOException{
		ClassPathResource resource = new ClassPathResource(filename);
		InputStream inputStream = resource.getInputStream();
		
		byte[] bdata = FileCopyUtils.copyToByteArray(inputStream);
		String data = new String(bdata, StandardCharsets.UTF_8);
		
		return data;
	}
	
	public static RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException{
		String privateKeyPEM = getKey(filename).trim();
		return getPrivateKeyFromString(privateKeyPEM);
	}
	
	public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException{
		String privateKeyPEM = key;
		privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
		privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
		byte[] encoded = Base64.decodeBase64(privateKeyPEM);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
		RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
		return privKey;
	}
	
	public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException{
		String publicKeyPEM = getKey(filename);
		return getPublicKeyFromString(publicKeyPEM);
	}
	
	public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException{
		String publicKeyPEM = key;
		publicKeyPEM = publicKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
		publicKeyPEM = publicKeyPEM.replace("-----END PRIVATE KEY-----", "");
		byte[] encoded = Base64.decodeBase64(publicKeyPEM);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
		return pubKey;
	}
	
	public static String sign(PrivateKey privateKey, String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException{
		Signature sign = Signature.getInstance("SHA1withRSA");
		sign.initSign(privateKey);
		sign.update(message.getBytes("UTF-8"));
		return new String(Base64.encodeBase64(sign.sign()), "UTF-8");
	}
	
	public static String encrypt(String rawText, PublicKey publicKey) throws IOException, GeneralSecurityException{
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		return Base64.encodeBase64String(cipher.doFinal(rawText.getBytes("UTF-8")));
	}
	
	public static String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException{
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
	}
}

 

4. TokenTest.java

위에서 확인하였던  Client ID / Client Secret / Service Account / Private Key 정보를 아래에 입력한다. Private Key 파일 위치는 통상 spring경로인 src > main > resources 아래에 두게되면 아래처럼 작성하면 된다.

import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.naverworks.message.util.RSAUtils;


public static void main(String[] args) throws Exception{
		
    Map<String, Object> headers = new HashMap<String, Object>();
    headers.put("alg", "RS256");
    headers.put("typ", "JWT");
    
    Date iat = new Date();
    Date exp = DateUtils.addMinutes(new Date(), 30);
    String grantType = URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer", StandardCharsets.UTF_8.toString());
		
    RSAPublicKey publicKey = null;
    RSAPrivateKey privateKey = RSAUtils.getPrivateKey("private.key");
    Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
		
    String assertion = JWT.create().withHeader(headers)
                                   .withIssuer("")  //Client ID
                                   .withSubject("") //Service Account
                                   .withIssuedAt(iat)
                                   .withExpiresAt(exp)
                                   .sign(algorithmRS);
		
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    params.add(new BasicNameValuePair("grant_type", grantType));
    params.add(new BasicNameValuePair("client_id", ""));       //Client ID
    params.add(new BasicNameValuePair("client_secret", ""));   //Client Secret
    params.add(new BasicNameValuePair("assertion", assertion));
    params.add(new BasicNameValuePair("scope", "bot"));
						    
    URI uri = new URI("https://auth.worksmobile.com/oauth2/v2.0/token");
    URIBuilder ub = new URIBuilder(uri);
		
    String contentType = "application/x-www-form-urlencoded";
		
    HttpClient httpClient = HttpClientBuilder.create().build();
		
    HttpPost http = new HttpPost(ub.toString());
    http.addHeader("Content-Type", contentType);
    http.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
		
    HttpResponse response = httpClient.execute(http);
    HttpEntity entity = response.getEntity();
    String responseStr = EntityUtils.toString(entity);
		
    Map<String, Object> responseMap = new ObjectMapper().readValue(responseStr, Map.class);
		
    String token = responseMap.get("access_token").toString();
    System.out.println(token);
		
}

 

 

5. 토큰 받아오기

위코드를 실행하면 아래와 같이 토큰값을 가져오게 된다.

naver works api token
naver works api token

 

 

 

Naver works message 보내기

 

1. 채널아이디(channelId)

- 아래와 같이 네이버웍스 메신저상에서 확인할수있다.

- 다른방법으로는 메신저 대화창에 /show roomid 를입력하면 채널ID가 제공된다.

 

naver works channel id
naver works channel id

 

2. 봇아이디 (botId)

naver works bot id
naver works bot id

 

 

3. MessageTest.java 메시지 보내기 테스트

위에서 취득한 채널아이디와 봇아이디를 입력하고 메시지 보내기 테스트를 해보자.

...
...

String botId = "0000000"; //봇아이디 입력
String channelId = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";  //채널아이디입력
String apiUrl = "https://www.worksapis.com/v1.0/bots/"+botId+"/channels/"+channelId+"/messages";

String text = "■업무전달 : 테스트업무 입니다. \\n";

String contents = "";
contents += "{\n \"type\" : \"flex\", \n" 
         + "\"altText\" : \"[테스트 입니다.]\", \n" 
         + "\"contents\" : { \n"
         + "\"type\" : \"bubble\", \n"	
         + "\"size\": \"kilo\", \n"
         + "\"body\" : { \n"		
         + "\"type\" : \"box\", \n"
         + "\"layout\" : \"vertical\", \n"
         + "\"contents\" : [ \n "
         + "{\n"		            
         +  "\"type\": \"text\", \n"
         +  "\"text\": \"[테스트 입니다.]\\n \", \n"
         +  "\"wrap\": true, \n"
         +  "\"size\": \"md\", \n"
         +  "\"weight\": \"bold\" \n"
         + "}, \n "	
         + "{ \n"		            
         +  "\"type\": \"text\", \n"
         +  "\"text\": \""+text+"\", \n"
         +  "\"wrap\": true, \n"
         +  "\"size\": \"xs\" \n"
         + "} \n "		
         + "] \n"  
         + "} \n"
         + "} \n"
         + "}";
		
HttpPost method  = new HttpPost(apiUrl);
method.setHeader("Authorization", "Bearer " + token);
method.setHeader("Content-Type","application/json; charset=UTF-8");

method.setEntity(new StringEntity("{ \"content\" : "+contents+" \r\n }", "UTF-8"));

CloseableHttpClient client = HttpClients.createDefault();
HttpResponse response = client.execute(method);

EntityUtils.toString(response.getEntity(), "UTF-8");

 

 

4.메시지봇 결과확인

아래와 같이 naver works에 메시지가 전달된다.

naver works message api
naver works message api