API Key 인증 방식
REST API를 요청할 때 HTTP 헤더에 Authorization 정보를 추가하여 인증받을 수 있습니다. API를 요청한 계정의 소유자를 확인하는데 필수적인 절차입니다.
솔라피 API Key 관리 페이지에서 API Key와 API Secret을 생성하여 REST API 호출에 사용하실 수 있습니다.
노트
Authorization 헤더 정보를 전달하는 방식은 HTTP에서 인증을 위한 수단으로 널리 사용되고 있습니다. Basic access authentication을 참고하세요.
Authorization 헤더 형식
기본 구조
Authorization: <AuthenticationMethod> apiKey=<API Key>, date=<Date Time>, salt=<Salt>, signature=<Signature>
예시
curl -X GET https://api.solapi.com/messages/v4/list \
--header "Authorization: HMAC-SHA256 apiKey=NCSAYU7YDBXYORXC, date=2019-07-01T00:41:48Z, salt=jqsba2jxjnrjor, signature=1779eac71a24cbeeadfa7263cb84b7ea0af1714f5c0270aa30ffd34600e363b4"
파라미터 설명
파라미터 | 설명 | 제약사항 |
---|---|---|
AuthenticationMethod | Signature 생성 알고리즘 | HMAC-SHA256 , HMAC-MD5 중 선택 |
API Key | 발급받은 API Key | 솔라피 콘솔에서 생성 |
Date Time | 요청 시간 | ISO 8601 규격 (예: 2019-07-01T00:41:48Z) |
Salt | 랜덤 문자열 | 12~64바이트의 불규칙적인 문자열 |
Signature | HMAC 해시코드 | Date Time + Salt를 데이터로 하고 API Secret을 Key로 생성 |
Signature 생성 방법
Signature는 다음과 같은 방식으로 생성됩니다:
- 데이터 준비:
<Date Time> + <Salt>
문자열 연결 - HMAC 생성: API Secret을 Key로 사용하여 HMAC 해시 생성
- 알고리즘: HMAC-SHA256 또는 HMAC-MD5 선택 가능
위험
API Secret은 Signature 생성 시에만 사용하고 외부에 노출되지 않도록 주의하세요.
보안 제약사항
제약사항 | 설명 | 목적 |
---|---|---|
시간 제한 | 서버 시간 기준 ±15분 이내 | Replay Attack 방지 |
중복 방지 | 15분 내 동일한 Signature 재사용 불가 | 재사용 공격 차단 |
Salt 변경 | 매 요청마다 다른 Salt 사용 필수 | Signature 고유성 보장 |
노트
메시지의 무결성 검증을 위해 Hash 기반의 MAC 알고리즘을 사용합니다. Hash-based message authentication code를 참고하세요.
언어별 구현 예제
Node.js 20.19.0
Signature 생성
const crypto = require('crypto');
// """HMAC-SHA256 시그니처 생성"""
function generateSignature(apiSecret, dateTime, salt) {
const data = dateTime + salt;
return crypto
.createHmac('sha256', apiSecret)
.update(data)
.digest('hex');
}
// """Authorization 헤더 생성"""
function createAuthHeader(apiKey, apiSecret) {
const dateTime = new Date().toISOString();
const salt = crypto.randomBytes(16).toString('hex');
const signature = generateSignature(apiSecret, dateTime, salt);
return `HMAC-SHA256 apiKey=${apiKey}, date=${dateTime}, salt=${salt}, signature=${signature}`;
}
API 요청 예제
const axios = require('axios');
async function sendMessage(apiKey, apiSecret, messageData) {
const authHeader = createAuthHeader(apiKey, apiSecret);
try {
const response = await axios.post('https://api.solapi.com/messages/v4/send-many/detail', messageData, {
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
console.error('API 요청 실패:', error.response?.data || error.message);
throw error;
}
}
JAVA 17+
Signature 생성
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
public class SolapiAuth {
// """HMAC-SHA256 시그니처 생성"""
public static String generateSignature(String apiSecret, String dateTime, String salt)
throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256"));
byte[] hash = mac.doFinal((dateTime + salt).getBytes());
return HexFormat.of().formatHex(hash);
}
// """Authorization 헤더 생성"""
public static String createAuthHeader(String apiKey, String apiSecret) throws Exception {
String dateTime = Instant.now().toString();
String salt = UUID.randomUUID().toString().replace("-", "");
String signature = generateSignature(apiSecret, dateTime, salt);
return "HMAC-SHA256 apiKey=%s, date=%s, salt=%s, signature=%s"
.formatted(apiKey, dateTime, salt, signature);
}
}
API 요청 예제
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class SolapiClient {
public static String sendMessage(String apiKey, String apiSecret, String messageJson)
throws Exception {
String authHeader = SolapiAuth.createAuthHeader(apiKey, apiSecret);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.solapi.com/messages/v4/send-many/detail"))
.header("Authorization", authHeader)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(messageJson))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return response.body();
}
}
Kotlin 2.1
Signature 생성
import java.time.Instant
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
object SolapiAuth {
/**
* HMAC-SHA256 시그니처 생성
*/
@Throws(Exception::class)
fun generateSignature(apiSecret: String, dateTime: String, salt: String): String {
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(apiSecret.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal((dateTime + salt).toByteArray())
return HexFormat.of().formatHex(hash)
}
/**
* Authorization 헤더 생성
*/
@Throws(Exception::class)
fun createAuthHeader(apiKey: String, apiSecret: String): String {
val dateTime = Instant.now().toString()
val salt = UUID.randomUUID().toString().replace("-", "")
val signature = generateSignature(apiSecret, dateTime, salt)
return "HMAC-SHA256 apiKey=$apiKey, date=$dateTime, salt=$salt, signature=$signature"
}
}
API 요청 예제
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
object SolapiClient {
@Throws(Exception::class)
fun sendMessage(apiKey: String, apiSecret: String, messageJson: String): String {
val authHeader = SolapiAuth.createAuthHeader(apiKey, apiSecret)
val client = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.solapi.com/messages/v4/send-many/detail"))
.header("Authorization", authHeader)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(messageJson))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
return response.body()
}
}
Python 3.12.0
Signature 생성
import hmac
import hashlib
import secrets
from datetime import datetime, timezone
def generate_signature(api_secret: str, date_time: str, salt: str) -> str:
"""HMAC-SHA256 시그니처 생성"""
data = date_time + salt
signature = hmac.new(
api_secret.encode(),
data.encode(),
hashlib.sha256
).hexdigest()
return signature
def create_auth_header(api_key: str, api_secret: str) -> str:
"""Authorization 헤더 생성"""
date_time = datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
salt = secrets.token_hex(16)
signature = generate_signature(api_secret, date_time, salt)
return f"HMAC-SHA256 apiKey={api_key}, date={date_time}, salt={salt}, signature={signature}"
API 요청 예제
import requests
from typing import Dict, Any
def send_message(api_key: str, api_secret: str, message_data: Dict[str, Any]) -> Dict[str, Any]:
"""메시지 전송 API 호출"""
auth_header = create_auth_header(api_key, api_secret)
headers = {
'Authorization': auth_header,
'Content-Type': 'application/json'
}
response = requests.post(
'https://api.solapi.com/messages/v4/send-many/detail',
json=message_data,
headers=headers
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
message_data = {
"message": {
"to": "01012345678",
"from": "01087654321",
"text": "테스트 메시지입니다."
}
}
PHP 8.4.11
Signature 생성
<?php
class SolapiAuth {
// HMAC-SHA256 시그니처 생성
public static function generateSignature($apiSecret, $dateTime, $salt) {
$data = $dateTime . $salt;
return hash_hmac('sha256', $data, $apiSecret);
}
// Authorization 헤더 생성
public static function createAuthHeader($apiKey, $apiSecret) {
$dateTime = gmdate('Y-m-d\TH:i:s\Z');
$salt = bin2hex(random_bytes(16));
$signature = self::generateSignature($apiSecret, $dateTime, $salt);
return "HMAC-SHA256 apiKey={$apiKey}, date={$dateTime}, salt={$salt}, signature={$signature}";
}
}
?>
API 요청 예제
<?php
function sendMessage($apiKey, $apiSecret, $messageData) {
$authHeader = SolapiAuth::createAuthHeader($apiKey, $apiSecret);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.solapi.com/messages/v4/send-many/detail',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($messageData),
CURLOPT_HTTPHEADER => [
'Authorization: ' . $authHeader,
'Content-Type: application/json'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("API 요청 실패: HTTP {$httpCode}");
}
return json_decode($response, true);
}
?>
Go 1.24.6
Signature 생성
package main
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
)
// HMAC-SHA256 시그니처 생성
func generateSignature(apiSecret, dateTime, salt string) string {
data := dateTime + salt
h := hmac.New(sha256.New, []byte(apiSecret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
// Authorization 헤더 생성
func createAuthHeader(apiKey, apiSecret string) (string, error) {
dateTime := time.Now().UTC().Format("2006-01-02T15:04:05Z")
saltBytes := make([]byte, 16)
if _, err := rand.Read(saltBytes); err != nil {
return "", err
}
salt := hex.EncodeToString(saltBytes)
signature := generateSignature(apiSecret, dateTime, salt)
return fmt.Sprintf("HMAC-SHA256 apiKey=%s, date=%s, salt=%s, signature=%s",
apiKey, dateTime, salt, signature), nil
}
API 요청 예제
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type MessageData struct {
Message struct {
To string `json:"to"`
From string `json:"from"`
Text string `json:"text"`
} `json:"message"`
}
func sendMessage(apiKey, apiSecret string, messageData MessageData) map[string]interface{} {
authHeader := createAuthHeader(apiKey, apiSecret)
jsonData, _ := json.Marshal(messageData)
req, _ := http.NewRequest("POST", "https://api.solapi.com/messages/v4/send-many/detail", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", authHeader)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result
}
오류 처리
API 인증 과정에서 발생할 수 있는 오류들과 대응 방법입니다.
오류 코드 | 설명 | HTTP 상태 코드 | 해결 방법 |
---|---|---|---|
InvalidAPIKey | 유효하지 않은 API Key | 403 | API Key 확인 및 재발급 |
SignatureDoesNotMatch | Signature 불일치 | 403 | Signature 생성 로직 검토 |
RequestTimeTooSkewed | 시간 차이 초과 (±15분) | 403 | 시스템 시간 동기화 |
DuplicatedSignature | 중복된 Signature 사용 | 403 | Salt 값 변경 및 재시도 |
시간 동기화
서버와 클라이언트 간의 시간 차이가 15분을 초과하면 인증이 실패합니다. 시스템 시간을 정확히 동기화해 주세요.