[ 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 ] mozilla 튜토리얼 예제로 살펴보는 Django 분석 ② - 1 : Locallibrary Models 디자인하기


LocalLibrary Model 디자인 하기


모델을 코딩하기 전에 
우선 어떤 데이터를 저장할지,
그리고 다른 객체들에 대한 관계를 
어떻게 지정할지에 대해 생각을 해봐야 한다.

이번 LocalLibrary 앱에는
책에 관한 정보들(제목, 저자, 분류 등)을 저장할 필요가 있다.

또한 모델을 디자인할 때 각각의 
객체(관련된 정보의 모임) 마다 
분리된 모델을 가지는 것이 좋을 것이다.
이 예시에서 확인할 수 있는 객체들은 
책, 책 인스턴스, 저자이다.

이렇게 모델과 필드를 결정하고 나면
각 객체들간의 관계에 대해 생각해봐야 한다.

Django에서는 관계에 대해
일 대 일(OneToOneField),
일 대 다(ForeignKey),
다 대 다(ManyToManyField) 
이 3가지로 설정할 수 있다.

mozilla 튜토리얼에서는 
아래와 같이 이러한 모델에 대한 
UML다이어그램을 제시해준다.


앞서 튜토리얼을 진행한 나로서는 
Book, Author, BookInstance 이렇게
3가지 model이 나오지 않을 까 추측해본다. 

모델 입문서


공식 도큐먼트의 튜토리얼에서 모델은 작성해봤지만
모델을 작성할 때의 
각 요소에 대한 설명은 자세히 되어있지 않았던 것이
아쉬웠었다.

그런 나의 아쉬움에 대답을 해주듯이 
이번 mozilla 튜토리얼에서는 
만족할 만큼 꽤나 자세히 적혀있었다.

따라서 이 모델에 관한 쪽은 
가능한 자세히 다루기로 하겠다.

①모델의 정의


Django에서 Model들은 보통 models.py 파일에서 정의된다.

이 Model들은 django.db.models.Model의 서브 클래스로 구현되며
필드, 메소드 그리고 메타데이터가 포함될 수 있다.

아래의 MyModelName클래스는 
전형적인 model들의 구성을 보여준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from django.db import models
 
class MyModelName(models.Model):
    """A typical class defining a model, derived from the Model class."""
 
    # Fields
    my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
    ...
 
    # Metadata
    class Meta: 
        ordering = ['-my_field_name']
 
    # Methods
    def get_absolute_url(self):
        """Returns the url to access a particular instance of MyModelName."""
        return reverse('model-detail-view', args=[str(self.id)])
    
    def __str__(self):
        """String for representing the MyModelName object (in Admin site etc.)."""
        return self.field_name
cs

1)필드(Fields)


모델은 모든 자료형의 필드를 가질 수 있다.

각 필드는 데이터베이스에 
저장하길 원하는 데이터 열(column)을 나타낸다.
각 데이터베이스 레코드는 
각 필드 값들 중 하나로 구성되어 있다.

위의 예제 중의 필드 코드를 살펴보자.

1
2
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
cs

위의 MyModelName 이라는 모델은 
model_my_field_name이라는 하나의 필드를 가지고 있고
model.CharField 타입 이다.

여러 자료형에 익숙한 개발자라면
이 필드는 문자열이 들어갈 변수라는 것을
추측해 볼 수 있을 것이다.

  • max_length = 20 : 필드의 길이를 설정한다.
    model_my_field는 영문자 20글자 인것을 알 수 있다.

  • help_text = ' Enter field documentation' :
    HTML 양식(Form)에서 사용자들에게 입력될 때 
    어떤 값을 입력해야 하는지 알려주기위한 텍스트 라벨이다.
필드의 이름은 쿼리 및 템플릿에서 참조할 때 사용되며,
인수로 지정된 라벨을 가지고 있거나
필드 변수의 이름 첫자를 대문자로 바꾸고
밑줄을 공백으로 바꿔서 기본 라벨을 추정할 수 있다.

예제의 my_field_name의 경우 
My field name을 기본 라벨로 가지고 있다.

2)일반적인 필드 인수


일반적으로 사용되는 필드의 인수는 아래와 같다.

  • help_text : HTML 양식(Form)에 대해 
    텍스트 라벨을 제공한다.

  • verbose_name : 필드 라벨 안에 사용되는 필드 이름이다.
    지정되어있지 않는다면, Django에서 지정한다.
    (위의 경우는 My field name을 기본 인수로 가진다.) 

  • default : 필드를 위한 기본값이다.
    여기에 들어가는 인수는 값 이나 객체 일 수 있다.
    이때 객체는 새로운 레코드가 생성될 때 마다 호출 된다.

  • null : null인수가 True라면,
    Django는 NULL값을 레코드로 저장한다.
    기본값은 False다.

  • blank : blank 인수가 True라면,
    해당 필드의 레코드 값을 비워두는 것을 허락한다.
    기본값은 False이며,
    이는 Django의 양식(Form)에 값을 입력하도록 강제한다는 것이다.

    이는 종종 null = True와 함께 사용되는데
    blank 값이 허용될 때,
    DB에서도 공백값을 적절하게 표시 할 수 있어야하기 때문이다.

  • choices : 필드를 위한 선택들의 모임이다.
    이 인수가 제공된다면,
    해당 기본 양식(form)  위젯은 표준 텍스트 필드가 아닌
    이 선택 항목을 가진 선택 상자가 된다.

  • primary_key : primary_key 인수가 True라면
    현재 필드를 모델의 primary key로 설정한다.
    만약 primary key로 지정된 필드가 없다면
    Django가 자동적으로 primary key를
    가지고 있는 필드를 생성한다.
이외의 필드는 아래의 공식 도큐먼트를 참고하길 바란다.

3) 일반적인 필드 타입


일반적인 필드 타입의 객체에 대해서는 아래와 같다.

  • CharField : 작은 고정된 길이의 문자열을 정의할 때 사용한다.
    저장하기 위해서는 max_length 인수를 정의해주어야 한다.

  • TextField : 긴 문자열을 정의할 때 사용한다.
    max_length 인수를 지정해야되지만,
    필드가 양식(form)안에 표시될 때 지정해도 된다.
    (DB 레벨에서는 강제되지 않기 때문)

  • IntegerField : 정수값을 정의할 때 사용한다.
    양식(form)에 입력된 값이 정수임을 검증하기도 한다.

  • DataField, DateTimeField : 날짜와 날짜 및 시간 정보를
    저장, 표현하는데 사용 한다.
    이 필드들은 모델이 저장될 때 마다 필드를 현재 날짜로 설정하기 위한 인수인 auto_now=True,
    모델이 처음 생성되었을 때만 날짜를 설정하기 위한 인수인 
    auto_now_add,
    그리고 사용자에 의해 변경될 수 있는 기본 날짜를 설정하기 위한 인수인
    default를 선언할 수 있다.

  • EmailField : 이메일 주소를 저장하고 검증하기 위해 사용한다.

  • FileField, ImageField : 파일과 이미지를 업로드하기 위해 사용된다.

    ImageField는 단지 파일이
    이미지임을 검증하는 기능이 추가로 있을 뿐이며,
    이 필드들은 업로드된 파일들이 어디에 어떻게 저장되는지를
    정의하는 매개변수를 가진다.

  • AutoField : 자동적으로 증가하는 IntegerField의 특별한 타입이다.
    이 타입의 primary key는 명시적으로 지정하지 않는 이상
    모델에 자동적으로 추가된다.

  • ForeignKey : 다른 model들과 일대다 관계를 지정하기 위해 사용된다.
    일대다에서 "일"쪽이 key를 포함하는 모델이다.

  • ManyToManyField : 다대다 관계를 지정하기 위해 사용된다.
    (예시 : 책은 여러 장르를 가질 수 있고,
    각각 장르에도 많은 책들이 있을 수 있다.)

    예제인 도서관 애플리케이션에서는
    이 필드를 ForeignKeys와 매우 유사하게 사용할 예정이다.

    하지만, 그룹 사이의 관계를 보여주기 위해서는
    더욱 복잡한 방식으로 사용될 수 있다.

    이것은 레코드가 삭제됐을 때 
    어떤 일이 일어나는지 정의하기 위해 
    on_delete 매개변수를 가진다.
이 외에도 많은 타입의 필드에 대해서는 아래의 
공식 도큐먼트 링크에서 참고하기 바란다.

4) 메타 데이터


아래와 같이 Class Meta를 선언하여
모델에 대한 모델-레벨에서 메타 데이터를 선언할 수 있다.

1
2
class Meta:
     ordering = ['-my_field_name']
cs

이 메타 데이터의 가장 유용한 기능 중 하나는
모델 타입의 쿼리문을 실행 할 때 반환되는 
기본 레코드 순서를 제어할 수 있다는 것이다.

이렇게 하려면 위와 같이 
필드 이름에 ordering 속성에 지정해야 한다.

순서는 필드의 타입에 따라 달라질 것이다.

위와 같이 오름 차순으로 정렬하고 싶다면
마이너스 기호(-)를 필드 이름 앞에 접두사로 붙이면 된다.

예를 들어,
기본적으로 아래와 같이 책들을 정렬하려고 한다면

1
ordering = ['title''-pubdate']
cs

책들은 A-Z까지 알파벳 순으로 정렬되고,
그 후에는 제목(title)안에 있는 발행일 별로 
가장 최근 것 부터 가장 오래된 것 순으로 정렬될 것 이다.

다른 일반적인 속성은 verbose_name이며,
단일 및 복수 형식의 클래스를 위한 이름 이다.

1
verbose_name = 'BetterName'
cs

이외의 모든 메타데이터 옵션에 대해서는 
아래의 공식 도큐먼트 링크에서 참고하길 바란다.


② 메소드(Methods)


모든 모델 마다 
표준 python 클래스인 __str__()을 정의하여
각 객체가 문자열을 리턴하도록 하자.
(java에서 생성자와 유사한 역활을 한다)

이 문자열은 관리자 사이트에 있는 
개별적인 레코드를 보여주는 데 사용된다.

1
2
def __str__(self):
    return self.field_name
cs

Django의 model에 포함시켜야 할 메소드는
get_absolute_url()이다.

웹 사이트의 개별적인 Model의 레코드를 
보여주기 위한 URL를 리턴해주는 메소드이다.
만약 이 메소드를 정의했다면 
Django는 자동으로 관리자 사이트 안의 
모델 레코드 수정 화면에 
"View on Site"버튼을 자동적으로 추가하게 할 것이다.

아래의 코드에서 get_absolute_url()의 
표준적인 사용 방법을 확인할 수 있다.

1
2
3
def get_absolute_url(self):
    """Returns the url to access a particular instance of the model."""
    return reverse('model-detail-view', args=[str(self.id)])
cs

만약 model의 개별적인 레코드를 보여주기 위해
/myapplication/mymodelname/2와 같은 
URL을 사용한다고 가정해보자.

이 경우 응답과 id를 'model-detail-view'에 전달하기 위해
URL Mapper를 만들어야 한다.

위의 reverse()함수에 알맞은 포맷의
URL을 생성하기 위해서 URL Mapper를 반전 시킬 수 있다.

물론 위의 코드는 아직 까지 
URL Mapping과 View, Template를 
작성하지 않았기 때문에 작동하지는 않는다.

③ 모델 관리(management)


Model Class들을 정의한 이후에는
Class들을 사용해서 레코드를 
생성, 업데이트, 삭제할 수 있고

모든 레코드와 레코드의 
특정 하위 집합을 가져오기 위해
쿼리를 실행할 수도 있다.

튜토리얼에서 View를 정의할 때
방법을 보여줄 것이지만,
아래와 같이 간략한 요약하기로 한다.

1) 레코드의 생성과 수정


레코드를 생성하려면
Model의 인스턴스를 정의하고 save()를 호출 할 수 있다.

1
2
3
4
5
# Create a new record using the model's constructor.
record = MyModelName(my_field_name="Instance #1")
 
# Save the object into the database.
record.save()
cs


만약 어떤 필드에서도 
primary_key를 선언하지 않았다면,
새로운 레코드는 자동적으로 
id라는 필드 이름을 가진 primary_key가 생성될 것이다.

위의 레코드를 저장한 후,
id 필드를 쿼리할 수 있는데
1의 값을 가질 것이다.

수정된 값들을 DB에 저장하기 위해서는 
save() 메소드를 호출해야 한다.

1
2
3
4
5
6
7
# Access model field values using Python attributes.
print(record.id) # should return 1 for the first record. 
print(record.my_field_name) # should print 'Instance #1'
 
# Change record by modifying the fields, then calling save().
record.my_field_name = "New Instance Name"
record.save()
cs

2) 레코드 검색하기


objects.all()을 사용하여 모델의 모든 레코드들을 
QuerySet으로 가져올 수 있다.

QuerySet은 반복 가능한 객체이며,
이는 반복할 수 있는 많은 객체들을 포함하고 있다는 의미이다.

1
all_books = Book.objects.all()
cs

Django의 filter()는 리턴된 QuerySet이 
지정된 문자 또는 숫자 필드를
특정 기준에 맞추어 필터링 할 수 있게 한다.

예를 들어 "wild"를 제목 안에 포함하는 책들을
필터링하여 그 숫자를 세어 보려면
아래와 같이 코딩하면 된다.

1
2
wild_books = Book.objects.filter(title__contains='wild')
number_wild_books = Book.objects.filter(title__contains='wild').count()
cs

기준이 될 필드와 타입은
filter() 메소드 안의 매개 변수에 의해 정의된다.

field_name__match_type을 사용해서 
위와 같이 대소문자를 구분하여 tilte을 필터링 할 수 있다.

대소문자를 구분하지 않는 icontains,
대소문자를 구분하지 않는 상태에서 정확히 일치하게 하는 iexact,
대소문자를 구분해 정확히 일치하게 하는 exact,

그리고 공식 도큐먼트 튜토리얼에서 정리했었던
아래와 같은 것들도 있다.

키워드

설명

__lt / __gt

작다 크다

__lt / __gte

작거나 같다작거나 크다

__in

리스트([1,2,3와 같은])

의 데이터와 동일한

검색

__year / __month / __day

일 검색

__isnull

Null인 값 검색

__contains

지정한 문자열을

포함하는 데이터를 검색

__icontains

지정한 문자열을

포함하는 데이터를 대소문자

구별하지 않고 검색

__startswith

지정한 문자열로 시작하는

데이터를 검색

__istartswith

지정한 문자열로 시작하는

데이터를 대소문자

구별하지 않고 검색

__range

문자숫자날짜의 범위를

지정해 검색


이외의 인수에 대해서는 
아래의 공식 도큐먼트를 참고하기 바란다.



또한 어떤 경우에는 
일대다 관계를 다른 Model에 정의하는 필드를 
필터링해야 할 때도 있다(ForeignKey의 경우)

이 경우에 추가적인 이중 밑줄을 사용하여
관련된 모델 안의 필드에 "색인(index)"할 수 있다.

예를 들어 특정한 장르 패턴을 가진 책들을 필터링하려면,
아래와 같이 genre 필드를 통해서
name에 색인(index)해야 한다.

1
2
# Will match on: Fiction, Science fiction, non-fiction etc.
books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')
cs

밑줄(__)을 사용하여 원하는 다양한 레벨의 관계
(ForeignKey / ManyToManyField)를 탐색할 수 있다.

예를 들어서 
추가적인 "cover"관계를 사용하여 정의된
다른 타입의 Model Book
다음과 같은 매개 변수 이름을 가질 것이다.
type__cover__name__exact='hard'

또한
관련된 모델의 역방향 검색, 필터 변경,
값의 더 작은 집합으로 리턴 등
쿼리로 할 수 있는 것들은 더 많이 존재 한다.

이외에 QuerySet에 대한 것들은 
아래 링크의 공식 도큐먼트를 참고하길 바란다.




이 블로그의 인기 게시물

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

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

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