[ Architecture, Technology ,Web ] SSO(Single Sign On) 그리고 SAML에 대해

이미지
이번 프로젝트 내부에서 어쩌다보니  유저 인증 관련 업무를 담당하게 되었고, 해야하는 업무는 내부에 사용했던 적이 없던  새로운 개발 플랫폼에서  SSO의 프로토콜 중  SAML을 이용해 앱의 인증을 구현해야만 했다. SSO를 생각해본적 조차 없는 상황에 이를 새로운 개발 플랫폼에 도입해야 했기 때문에 많은 시행착오를 겪었으나 구현에 성공하였으며 덕분에 SSO에 대한 전반적인 지식을 쌓을 수 있었다. 이번에는 그러한 과정에서 나온 지식들과 경험을  공유하고자 한다. SSO에 대한 정의 먼저 사전적 정의 부터 살펴보자. 다만, 기술적인 용어다보니 자주 사용하는 옥스포드 사전에 정의를 찾을 수 없기 때문에  검색으로 찾을 수 있는 정의를 몇 가지 살펴보고 교차 검증을 해보자. 첫 번째 정의를 살펴보자. Single sign-on (SSO) is an identification method that enables users to log in to multiple applications and websites with one set of credentials.  SSO는 웹사이트에서 한 번의 인증(one set of credentials)으로 복수의 어플리케이션에 로그인 할 수 있는 인증(identification) 방법(method) 이다. 두 번째는 위키피디아의 정의이다. Single sign-on (SSO) is an authentication scheme that allows a user to log in with a single ID to any of several related, yet independent, software systems. SSO는 독립적이지만 연관되어있는 몇몇 소프트웨어에 대해 하나의 ID로 로그인을 할 수 있도록 하는 인증 구조(scheme) 세부 설명에 조금 차이가 있어 보이지만 전체적인 틀은 매우 비슷해 보인다.  몇 가지 포인트가 되는 단어를 추출해 이를 연결해보자면 아래와 같은 의미를 산출 할 수 있다. 독립적이지만 연관되어 있

[ Django, Python ] ASGI와 WSGI 분석(Analysis of ASGI and WSGI)

ASGI? WSGI?


Django의 프로젝트를 생성 할 때 
같이 생성되는 파일들을 살펴보다가 2가지 파일에 대한 의문점이 생겼다.


바로 asgi.py 파일과 wsgi.py 파일에 대해서 이다.

Django 공식 도큐먼트 사이트에서는 아래의 사진과 같이 해당 파일들을 설명하고 있다.

이게 무슨 말인가 싶어 서둘러 구글링을 통해 조사해봤다.


위의 도큐먼트에 따르면,
ASGI WSGI의 상위 호환으로 web server와 프레임워크(django), 
애플리케이션을 연결 해주는 Python의 표준 API라고 한다.

, 웹 서버, 프레임 워크, 어플리케이션을 연결해주는 표준 인터페이스 인듯 하다.

이어서 WSGI ASGI이전의 Python의 표준인 것으로 보인다.

해당 글만 보고 판단해 봤을때 
기존 WSGI에서는 비동기 방식의 기능 
구현에 있어서는 문제가 많았던 것으로 보인다.

따라서 이런 WSGI의 문제점을 보완하기 위해서 
새로운 인터페이스를 개발해야할 필요성이 생겼고
이에 대한 솔루션으로 ASGI가 나온 것으로 보인다.

ASGI(Asynchronous Server Gateway
Interface)


ASGI의 공식 도큐먼트(https://asgi.readthedocs.io/en/latest/)에 따르면

ASGIWSGI의 하위 호환성을 포함한 
다양한 서버 및 애플리케이션 프레임 워크의 
비동기 및 동기 기능의 표준을 제공해 준다고 한다.

그리고 이 ASGI의 사양서(Specification)에는 
아래와 같이 어플리케이션(API)이 구현되어야 한다고 설명한다.


아무래도 개발자로서 알아야 할 부분은 scope인 듯 하다.



사양서에서는 scope에 대해 위와 같이 설명하고 있다.

scope에서는 여러가지 매개변수를 넣을 수 있는 듯 한데
사양서에서 설명하는 것은 일단 scope의 매개변수 중 
type(보통 http)이라는 매개변수는 무조건 존재하는 하며,
해당 표준이 ASGI냐 아니면 WSGI이냐를 선택 할 수 있는 듯 하다.

또한 [“spec_version”]이라는 매개변수를 통해 ASGI의 버전도 선택이 가능하다.

따라서 버전이 갱신됨에 따라 생길 수 있는 
함수 명 변경되어 시스템 전체에 오류가 나오는 등의
호환성에 대해서는 큰 문제는 없을 것 같다.

실제 코드가 작성되어 있는데
scope의 매개변수는 아래와 같은 종류가 있다고 한다.


자세한 내용을 링크  쪽을 참고하길 바란다.

WSGI(Web Server Gateway Interface)


그렇다면 여기서 좀 더 나아가 WSGI에 대해 
조금 자세히 이야기해보자.

WSGI는 서버 사이드의 서버 API이다.

위에서도 잠깐 언급했다시피 
WSGI가 나오기 이전에는 Python의 웹 애플리케이션 프레임워크에서 
웹서버를 선택할 때 제약이 있었다고 한다.

보통 CGI, FastCGI, mod_python 중에 
하나만 선택 할 수 있도록 설계되었다는데,

로우 레벨로 만들어진 WSGI가 등장함으로써
웹서버와 웹 어플리케이션, 프레임 워크 간에 벽이 허물어 졌다고 한다.

WSGI[서버와 게이트웨이], [애플리케이션과 프레임워크] 양 단으로 나뉘어져 있는데,

WSGI에서 리퀘스트(요청)를 처리하면
서버 단에서 환경 정보와 콜백 함수를 에플리케이션 단에 제공해야 한다.

애플리케이션(Django와 같은)은 
그 요청을 처리하고 미리 제공된 콜백 함수를 통해 서버단에 응답해준다.

이때 WSGI 미들웨어 WSGI서버와 애플리케이션 사이를 중계해주는데,

이 미들웨어는 서버 관점에서 애플리케이션으로
애플리케이션 관점에서는 서버로서 행동 한다.

정리 해보자면 아래와 같은 과정을 거친다고 할 수 있다.

요청 → 웹서버(Apachi, GWS등) → WSGI 미들웨어 → WSGI를 지원하는 앱어플리케이션 프레임워크 (Django 등)

이 미들웨어는 아래와 같은 기능을 가진다.

ASGI와 WSGI는 왜 필요한가?


그렇다면 ASGI와 WSGI가 
왜 필요한가에 대해서도 궁금할 것이다.

적어도 IT업계에서 이유 없는 행동은 있을 수 없다.

물론 궁금하지 않은 사람도 있을태지만 말이다.

어쨋든 ASGI와 WSGI가 왜 필요한가에 대해서는
먼저 Django 애플리케이션 서버 아키텍처를 살펴볼 필요가 있다.

 
Django 웹 애플리케이션 서버 아키텍처에서는 
빨간색 박스와 같이
Django 프레임워크 내부의 URLWeb Server가 통신하게 된다.


이 사이에 Django 프레임워크 안에 미들웨어로서 
ASGI 또는 WSGI가 존재한다.

이쯤되면 굳이 이런 미들웨어가 필요하지?
라는 의문이 들 것이다.

이유는 간단하다.

Python 코드를 읽을 수 없을 뿐더러 
굳이 불필요한 코드를 전송해서 비용을 증가 시킬 이유가 없기 때문이다.

예컨데, 흔히 웹 서버로 사용되어져 있는 
Apach 재단의 Apach HTTP Server와 Tomcat은 
기본적으로 Java기반이므로 Python코드를 읽을 수 없다.

그렇기 때문에 과거에는 사용할 수 있는 
웹 서버는 매우 한정적일 수 밖에 없다.

물론 그 한정적인 웹 서버를 사용하면 좋겠지만,
그럴 경우 여러가지 비용이 증가하기 때문에

성능면에서도 검증이 되어 있고
모두가 익숙한 Apach 쪽의 웹 서버를 이용하는게 좋다.

하지만 Apach 재단에서 
굳이 Python코드를 읽게 끔 해줄 이유는 없다.

물론 현재에 와서는 아파치 모듈에 
mod_python과 mod_wsgi가 추가됨으로써 문제는 사라졌지만 말이다.

따라서 최적의 솔루션은 Apach 재단의 웹 서버 뿐만 아니라 
모든 웹 서버와 Python 계열의 프레임 워크가 
통신할 수 있게 해주는 미들 웨어(인터페이스) 필요한 것이다.

이에 대한 솔루션으로 나온 것이 WSGIASGI이다. 


Python의 공식 페이지에서도 이와 관련된 이야기를
위와 같이 이에 대해 명시하고 있다.

자세한 이야기는 아래의 레퍼런스의 링크를 참고하길 바란다.

따라서 이런 미들웨어 API가 개발됨으로써
Python 계열의 프레임 워크 들은 더 이상 
Web Server에 대해 신경 써야 할 일이 줄어 들었다. 

WSGI는 어떻게 움직이는가?

그렇다면 이러한 궁금증도 머리 속에 떠오를 것이다

WSGI는 어떻게 움직이고 있을까?라는 생각이다.

물론 단순히 코드 안의 세계에만 생각하고 움직이는
개발자라면 알 필요는 없을 것이다.

하지만 더 위를 목표로 하고 있고,
코드의 세계에서 벗어나 진정한 해결사로서,

엔지니어로서 살아가고 싶다면 
코드에 대해서는 혹은 구현 방법에 대해서 
세세히 알 필요는 없지만,
어떻게 움직이고 있는지 정도는 알아야 할 필요가 있다.

그래야만 해결사라고 할 수 있지 않겠는가?

어쨋든 이 WSGI는 Python 에서 제시한 PEP 333 중에 한 항목이다.

WSGI는 위에서 언급했듯이 
Python 기반의 웹 애플리케이션 서버와 웹 서버 간의 통신 프로토콜이다. 

이 WSGI는 콜백(Callbacks)으로 작동하는데,
서버가 각 요청에 대해 호출하는 기능을 제공한다.

WSGI가 작동하는 방식은 
WSGI와 웹 서버(Web Server)가 작동하는 방식과
WSGI와 웹 애플리케이션 서버(Web Framework, App 등)가 
작동하는 방식으로 나눌 수 있다.

① WSGI와 웹 서버(Web Server) 측(Side)


웹 서버는 2가지를 제공해야 한다.
사전 형(Dictionary) 변수 environ,
그리고 start_response 함수(Function)이다.

먼저 사전 형(Dictionary) 변수 environ안에는 
CGI환경과 비슷한 데이터(usual things)들이 포함되어 있어야 한다.
(여기서 CGI환경에 대한 이야기는 추후에 이야기하기로 하겠다.)

다음으로 start_response 함수(Function)안에는 두 가지 인수(Arguments)
String 형의 표준 HTTP 상태 값인 
200 OK, 404 not found 등이 값으로 들어가 있는 status 인수와 
표준 HTTP 리스폰 헤더(response headers)의 리스트 값(Key-Value)이 들어있는
response_headers 콜백(Callback)인수가 있다. 

예컨데 다음과 같은 코드를 통해
Web Server는 애플리케이션(WSGI)을 호출하여
웹 애플리케이션 서버(Web Framework, App 등)에 요청(Request)을 할 수 있다.

1
2
3
iterable = app(environ, start_response)
for data in interable:
    # send data to clinet
cs

해더를 빌드해 start_response를 호출하고 
iterable에서 반환된 데이터를 빌드하는 것은 
웹 애플리케이션 서버(Web Framework, App 등)

HTTP를 통해 헤더와 데이터 모두를 제공하는 것은 
웹 서버(Web Server)에 책임이 있다.

② WSGI와 웹 애플리케이션 서버 측(Side)


웹 애플리케이션 서버(Web Framework, App 등)는 
호출 가능한 클래스(Class), 객체(Objacts), 함수(Function) 형식으로 
서버에 표시되는데 

__init__, __call__ 또는 이외의 함수(Function) 들은 
environ 객체와 호출 가능한 start_response가 포함되어 있어야 한다.

웹 애플리케이션 서버는 데이터를 반환하거나 생성하기 전에 
start_response를 호출해야 하며,

return [ pages ]와 같은 형식으로
모든 데이터를 반복 가능한 형식으로 반환해야 한다.

WSGI 애플리케이션의 예


이를 기반으로 하여 아래와 같은

1
2
3
4
5
6
7
class Upperware : 
   def __init __ (self, app) : 
      self.wrapped_app = app 
 
   def __call __ (self, environ, start_response) : 
      for data in self.wrapped_app (environ, start_response) : 
         return data.upper ()
cs

전송된 데이터를 대문자로 표시하는 
매우 간단한 미들웨어 앱을 작성할 수 있다.

이를 서버 측에서는

1
2
Wrapped_app = Upperware (simple_app) 
serve (wrapped_app)
cs

위와 같은 코드를 통해 Upperware 앱을 실행 할 수 있다.

물론 실제 개발된 WSGI 미들웨어는 이보다 더 복잡할 것이다.




참고 : 









이 블로그의 인기 게시물

[ Web ] 웹 애플리케이션 아키텍처 (Web Application Architecture)

[ Web ] 웹 애플리케이션? 웹 사이트?(Web Application? Web Site?)

[ Web ] 서버 사이드(Sever Side) ? 클라이언트 사이드(Client Side)? 1 [서론, 클라이언트 사이드(Client Side)]