HTTP 요청 헤더로 인증 정보를 전송하는 방법
WebSocket 인터페이스의 인증 정보(API key)는 s 명령어 내에서 authorization={API_KEY}와 같이 설정하는 방법뿐만 아니라, WebSocket 연결 시의 HTTP 요청 헤더에 기술하는 것도 가능합니다. 여기서는 그 방법을 설명합니다.
방법 1: Authorization 헤더를 사용하는 방법
WebSocket 연결 시작 시의 HTTP 요청 헤더에 다음과 같이 인증용 커스텀 헤더를 기술합니다.
Authorization: Bearer {API_KEY}
커스텀 헤더의 지정 방법은 사용하는 클라이언트 라이브러리나 실행 환경에 따라 다릅니다.
예를 들어, WebSocket 인터페이스에서 소개한 Python 샘플 코드를 이 형식으로 다시 작성하는 경우, 다음과 같이 header 인수를 추가합니다. 또한 s 명령어 내의 인증 정보에 관한 기술은 삭제합니다.
ws = websocket.WebSocketApp('wss://acp-api.amivoice.com/v1/',
header=["Authorization: Bearer {API_KEY}"],
on_open=on_open,
on_message=on_message,
on_close=on_close)
방법 2: Sec-WebSocket-Protocol 헤더를 사용하는 방법
방법 1의 Authorization 헤더를 사용할 수 없는 경우에도, AmiVoice API에서는 서브프로토콜 협상 헤더를 인증 정보 전송에 이용할 수 있습니다.
이 경우 HTTP 요청 헤더에 다음과 같이 기술합니다.
Sec-WebSocket-Protocol: wrp, {API_KEY}
예를 들어, WebSocket 인터페이스에서 소개한 Python 샘플 코드를 이 형식으로 다시 작성하는 경우, 다음과 같이 서브프로토콜을 지정하는 인수를 추가합니다. 또한 s 명령어 내의 인증 정보에 관한 기술은 삭제합니다.
ws = websocket.WebSocketApp('wss://acp-api.amivoice.com/v1/',
subprotocols=["wrp", "{API_KEY}"],
on_open=on_open,
on_message=on_message,
on_close=on_close)
Sec-WebSocket-Protocol 헤더를 이용하는 방법은 브라우저 JavaScript 등 Authorization 헤더를 직접 지정할 수 없는 환경에서도 이용할 수 있습니다. 다만, 이 방법으로 브라우저에서 직접 연결하는 경우, API key가 클라이언트 측 코드 또는 실행 환경에서 참조 가능해진다는 점에 주의하십시오. API key를 최종 사용자에게 배포하지 않으려면, 프록시 서버 등 서버 측에서 AmiVoice API에 연결하십시오.
인증 정보의 우선순위
여러 방법으로 인증 정보를 기술한 경우, 실제 인증에는 다음 우선순위로 이용됩니다.
s명령어 내의authorization={API_KEY}Authorization: Bearer {API_KEY}헤더 (방법 1)Sec-WebSocket-Protocol: wrp, {API_KEY}헤더 (방법 2)
HTTP 요청 헤더에 기술한 인증 정보를 이용하려면, s 명령어 내에 인증 정보(authorization)를 기술하지 마십시오.
프록시 서버에서 API key를 관리하기
보안을 위해 클라이언트 단말에 API key를 배포하고 싶지 않은 경우, 프록시 서버 등의 서버 측 컴포넌트에서 API key를 관리하고, 그 서버 측 컴포넌트에서 AmiVoice API에 연결하며, 클라이언트 단말은 서버에 연결하도록 하는 구성을 생각할 수 있습니다.
그러나 WebSocket 인터페이스의 경우, WebSocket 세션 확립 후에 s 명령어로 API key를 전송하는 방법이라면, 프록시 서버가 WebSocket을 중계하기만 하는 구성에서는 도중에 API key를 부여할 수 없어, 클라이언트 단말에 API key를 보유하게 하는 것이 필요해집니다.
이러한 경우에 활용할 수 있는 것이 여기서 설명한 인증 정보 전송 방법입니다. 프록시 서버 측에서 AmiVoice API로의 WebSocket 연결을 확립할 때, 연결 시의 HTTP 요청 헤더에 API key를 설정함으로써, 최종 사용자의 클라이언트 단말에 API key를 배포하지 않는 구성을 실현할 수 있습니다.
또한 애플리케이션이 브라우저 JavaScript 등 Authorization 헤더를 직접 지정할 수 없는 환경인 경우에도, 프록시 서버에서 AmiVoice API로의 WebSocket 연결을 수행하는 경우에는 방법 1을 이용할 수 있습니다.
샘플 코드
WebSocket 인터페이스의 샘플 코드를 방법 1을 이용하는 형태로 다시 작성한 것입니다. 다시 작성한 부분은 하이라이트되어 있습니다.
import time
import websocket
import json
import threading
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(threadName)s %(message)s")
server = 'wss://acp-api.amivoice.com/v1/'
header = ["Authorization: Bearer {API_KEY}"]
filename = 'test.wav'
codec = "16K"
audio_block_size = 16000
grammar_file_names = "-a-general"
options = {
"profileId" : "",
"profileWords" : "",
"keepFillerToken": "",
"resultUpdatedInterval" : "1000",
}
def on_open(ws):
logger.info("open")
def start(*args):
command = "s {} {}".format(codec, grammar_file_names)
for k, v in options.items():
if v != "":
if k == 'profileWords':
v = '"' + v.replace('"', '""') + '"'
command += f" {k}={v}"
logger.info(f"send> {command}")
ws.send(command)
threading.Thread(target=start).start()
def on_message(ws, message):
event = message[0]
content = message[2:].rstrip()
logger.info(f"message: {event} {content}")
if event == 's':
if content == "can't connect to recognizer server":
logger.error(content)
return
def send_audio(*args):
with open(filename, mode='rb') as file:
buf = file.read(audio_block_size)
while buf:
logger.debug("send> p [..({} bytes)..]".format(len(buf)))
ws.send(b'p' + buf,
opcode=websocket.ABNF.OPCODE_BINARY)
buf = file.read(audio_block_size)
time.sleep(0.5)
logger.info("send> e")
ws.send('e')
threading.Thread(target=send_audio).start()
elif event == 'G':
pass
elif event == 'S':
starttime = int(content)
elif event == 'E':
endtime = int(content)
elif event == 'C':
pass
elif event == 'U':
raw = json.loads(content) if content else ''
elif event == 'A' or event == 'R':
raw = json.loads(content) if content else ''
elif event == 'e':
logger.info("close>")
ws.close()
def on_close(ws):
logger.info("close")
logger.info("open> {}".format(server))
ws = websocket.WebSocketApp(server,
header=header,
on_open=on_open,
on_message=on_message,
on_close=on_close)
ws.run_forever()