My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


쟝고(Django) 4편 - 로그인

1) 유저 로그인기능

1-1. Statelesss

HTML은 기본적으로 상태에 대한 정의를 하지 않기 때문에(Stateless)

특정 유저라고 해도 HTML의 갑자기 상태가 변해서 특별한 권한을 제공하지는 않는다.

그래서 쟝고가 로그인한 유저에게 특별한 권한을 제공하기 위하여 django.contrib.auth에서 여러가지 인증기능을 제공한다.

쟝고에 대해서 알아보기 이전에 유저 검증방법들을 대략적으로 알아보도록 하자.

1-2. 일반적으로 유저를 검증하는 방법

  1. 쿠키

    1. 검증된 사람에게는 쿠키를 넣어놓음.
    2. 검증 이후에는 쿠키를 같이 넣어서 요청을 보내면 로그인되었다고 생각함.
    3. 단점 : 남의 쿠키를 탈취할 수 있음(프록시)
  2. 세션

    1. 임시 데이터베이스를 만듬 (세션)

      • 각각의 쿠키값들을 유저마다 다르게 설정함.
      • 추가로 쿠키이외에 무언가를 추가한다.
    2. 유효한 세션이 아닙니다 & 세션이 만료되었습니다

      유저의 상태를 지속적으로 트래킹하기때문에 나타나는 것

      세션만료기간을 따로 설정해서 쿠키의 지속시간을 설정함.

    -> 간단하게 요약 : 로그인 = 유저의 정보를 세션에 집어넣는다!

2) Django에서 로그인기능을 구현하기

django.contrib.auth를 이용할 수 있다

  1. Django에 유저를 등록한다.

    python manage.py createsuperuser
    

    이 스크립트를 통해서 유저를 등록하면, 쟝고에서 별도로 관리하는 데이터베이스에 정보가 기록된다. 이는 django.contrib.auth.models의 User 클래스와 연결되어있다!

    물론 password가 12341234라고 해서 User클래스에 12341234라고 입력되는건 아니다. 왜냐하면 해쉬함수를 통해서 암호화되기 때문인데, 해쉬함수에 대해서는 뒤에 설명하도록 하겠다.

    User 클래스에 대한 정보는 이 링크에서 확인할 수 있다.

  2. Login form을 만든다.

    여기서는 password와 username을 받는다.

       
    {% extends 'todos/base.html' %}
    {% block content %}
        <h1 class="text-center">login</h1>
           
        <div class="row">
            <div class="col-md-6 offset-md-3">
                <form action="" method="POST">
                  {% csrf_token %}
                  <div class="form-group">
                    <label for="username">Username: </label>
                    <input type="text" name="username" id="username" class="form-control" placeholder="Enter username">
                  </div>
                  <div class="form-group">
                    <label for="password">Password: </label>
                    <input type="password" name="password" id="password" class="form-control" placeholder="Password">
                  </div>
                  <button type="submit" class="btn btn-primary">Submit</button>
                </form>    
            </div>
        </div>
    {% endblock %}
       
    

    11

  3. 로그인 처리 - Django 입장

    클라이언트가 보낸 Username과 Password를 이용한다.

    • authenticate : 받은 데이터가 User 테이블에 존재하는지 확인한다.
    • login : 받은 데이터를 적절히 잘 편집해서 임시 데이터베이스(세션)에 추가한다.
    from django.contrib.auth import authenticate, login
       
    username = request.POST.get('username')
    password = request.POST.get('password')
    user = authenticate(request, username=username, password=password)
    if user:
        login(request, user)
        return redirect('todos:home')
    else:
        return redirect('users:login')
    
  4. 로그인 처리 - 클라이언트 입장

    클라이언트 역시 자기가 로그인이 되었다는 것을 알아야한다

    로그인이 되었다는 메시지를 따로 보내면 좋을 것 같다. 이를테면, 이런식으로

    12

    쟝고에서 유저에게 메세지를 보내는 기능을 제공한다.

    1. 메세지 생성

      from django.contrib import message

      message객체를 통해서 메세지를 생성할 수 있다.

      from django.contrib import messages
      # 생략..
      if user:
          login(request, user)
          # 유저에게 성공적으로 로그인이 되었다는 것을 알려준다.
          messages.success(request, '성공적으로 로그인 되었습니다.')
          return redirect('todos:home')
      else:
          messages.success(request, '로그인에 실패하였습니다.')
          return redirect('users:login')
      

      success라는 클래스는 말그대로 성공했다는 의미인데, 이건 bootstrap의 success 색깔과 연결된다고 한다. 이에 대해서는 나중에 다뤄보도록 하자.

    2. 생성한 메세지를 저장해야한다.

      settings.py에서 메세지를 저장할 곳을 지정해주면 자동으로 messages라는 객체에 저장된다.

      # 메세지를 어떻게 활용할지를 정확히 명시
      # 쓰고있는 메세지는 세션을 통해서 활용하게 될테니 이 위치를 알리자.
      MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
      
    3. 메세지 출력하기

      Bootstrap의 alert 컴포넌트를 이용한다.

            
      {% if messages %}
      <div class="alert alert-warning alert-dismissible fade show" role="alert">
          {% for message in messages %}
          <p>{{ message }}</p>
          {% endfor %}
          <button type="button" class="close" data-dismiss="alert" aria-label="Close">
              <span aria-hidden="true">&times;</span>
          </button>
      </div>
      {% endif %}
            
      

      메세지가 존재할때만 alert 컴포넌트를 남겨주는것이 중요하다.

  5. 로그아웃

    • 임시 데이터베이스(세션)에 있는 유저의 데이터를 제거하는 작업을 수행하면 된다.

      Django가 logout이라는 함수를 지원한다.

    from django.contrib.auth import logout
       
    def logout_user(request):
        logout(request)
        messages.success(request, '로그아웃되었습니다.')
        return redirect('todos:home')
    

    13

  6. 유저를 DTL(Django template language)에서 인식하기

    참고링크 : https://docs.djangoproject.com/en/dev/topics/auth/default/#authentication-data-in-templates

    Django api reference에서는 이렇게 설명되어 있다.

    14

    ReauestContext라는 녀석은 뭔지 잘 모르겠지만.. 밑을 읽어보면 현재 로그인 중인 유저(User의 인스턴스)나, 익명 유저의 정보는 DTL에서 user라는 변수에 저장되어있다고 한다.

    따라서, 이 user라는 변수의 is_authenticated라는 멤버변수를 이용해서 익명유저인지 아닌지 확인할 수 있다.

       
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav ml-auto">
            {% if user.is_authenticated %}
            <li class="nav-item">
                <a class="nav-link" href="{% url 'users:profile' %}">Profile</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'users:logout' %}">Logout</a>
            </li>
            {% else %}
            <li class="nav-item">
                <a class="nav-link" href="{% url 'users:login' %}">Login</a>
            </li>
            {% endif %}
        </ul>
    </div>
       
    

3) Django가 Password를 처리하는 방식

비밀번호를 hash를 통해 암호화시킨다.

  1. hash함수는 역함수가 없기 때문에 hash를 알아낸다고 해도 비밀번호를 알 수는 없다.

  2. 패스워드를 그대로 저장하면 법에 걸림……

    (쟝고에 그대로 맡기면 패스워드를 암호화해서 저장해줌.)

  3. 쟝고의 암호화알고리즘

    sha256

    1. hash function

      • 임의의 길이의 input
      • 고정된 길이의 output (hash, digest) : 데이터를 hash function을 통과시켜 요약함.
      • output으로 input으로 추측할 수 없어야함.
      • output은 확률적으로 거의 랜덤하게 만들어져야함.
      • hash는 암호화뿐만 아니라 data의 무결성을 검증하는데 많이 쓰임
    2. 데이터의 무결성

      데이터가 조금만 변화되어도 hash값이 엄청나게 많이 바뀌기 때문에 이 데이터가 탈취되어서 변경되었는지 아닌지를 비효율적인 kmp알고리즘보다 hash함수를 통해서 변화할 수 있다.

      예를 들어서, git에서도 해쉬값을 통해서 프로젝트의 상태를 트래킹하는데,

      그 프로젝트에서 한글자만 바뀌어도 해쉬가 많이 바뀌기 때문에 깃이 귀신같이 알아차리게 되는 것이다.

      $ git hash-object dtd/이진수.py
      23e882b0ac76787c8b2e125ca2b1fb9dbddba2f0
      

      아무리 긴 길이의 문자열이어도 hash함수로는 64개의 문자열(16비트)로 대응되기 때문에 짧은 시간 안에 판단할 수 있다.

    추가로 salt값을 뿌려서 hash값을 교란시키는 작업도 수행함.

comments powered by Disqus