숙제: 안전한 웹사이트 만들기

관리자 화면을 사용하는 경우를 제외하고는 미리 설정한 비밀번호를 그동안 사용하지 않았어요. 그래서 내 블로그에 누구든지 포스트를 마음대로 작성하거나 수정할 수 있다는 것을 눈치챘을 거에요. '너가 누군지 모르겠지만, 내 블로그에 글쓰는 것을 원치않아!'라는 생각이 들거에요. 그래서 아무나 들어와서 글을 쓰지 못하도록 뭔가 만들 수 있답니다.

포스트 작성/수정 편집 허가하기

첫번째로 보안을 위해 무언가를 할거에요. 우리는 로그인 한 사용자만 포스트를 접근할 수 있도록 post_new, post_edit, post_draft_list, post_remove and post_publish의 뷰들을 보호할 거에요. Django는 decorators 을 사용해 도움을 주고 있습니다. <0>decorators는 고급 주제이긴 합니다. 지금 기술적인 것에 걱정하지 마세요. 여러분들은 나중에 이해하게 될 거에요. Django에서 제공하는 django.contrib.auth.decorators 모듈 안의 decorator를 사용하는데, 이 것을 login_required 라고 합니다..

그 다음, blog/views.py 을 수정하고 import를 하는 영역에 아래와 같이 한 줄을 추가합니다. :

from django.contrib.auth.decorators import login_required

그리고 각각의 post_new, post_edit, post_draft_list, post_remove 그리고 post_publish 뷰의 앞에 아래와 같이 줄을 추가합니다.

@login_required
def post_new(request):
    [...]

잘했어요! 이제 http://localhost:8000/post/new/ 로 접속해보세요. 다른 점을 찾으셨나요?

만약 비어있는 폼 페이지만 보인다면, 이미 관리자 화면을 통해 로그인을 한 상태일 거에요. http://localhost:8000/admin/logout/ 로 로그아웃을 한 다음, http://localhost:8000/post/new 로 다시 접속해보세요.

사랑스런 에러들 중 하나를 보게 될 거에요. 정말 흥미로운 사실 중 하나: 추가한 Decorator는 로그인 페이지로 리디렉션 될 거에요. 아직 사용할 수 없지만, "페이지를 찾을 수 없습니다.(404)" 라는 에러를 나타나죠.

post_edit, post_remove, post_draft_list, post_publish 위에도 decorator를 추가하는 것을 잊지 마세요.

야호, 목표지점 까지 다 왔어요! 이제 다른 사람들이 내 블로그에 더 이상 마음대로 포스트를 쓸 수 없답니다. 그런데 관리자인 나도 새 글을 작성할 수가 없네요. 그럼 작성할 수 있게 수정해 봅시다.

사용자 로그인

이제 우리는 사용자와 패스워드 그리고 인증을 구현할 수 있는 많은 종류의 마법의 도구를 사용할 수 있었어요. 그러나 모든 도구들을 정확히 사용하는 것은 꽤 복잡하지요. Django에서 제공되는 인증 도구를 좀 더 사용할 수 있게 만들 거에요.

Mysite/urls.py 에서 url(r'^accounts/login/$', 'django.contrib.auth.views.login') 부분에 url을 추가 합니다. 아래처럼 말이죠. :

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^accounts/login/$', 'django.contrib.auth.views.login'),
    url(r'', include('blog.urls')),
)

다음으로는 로그인 페이지 템플릿이 필요합니다. blog/templates/registration 디렉토리를 생성하고, login.html 파일을 그 디렉토리 안에 넣으세요.

{% extends "blog/base.html" %}

{% block content %}

{% if form.errors %}
<p>이름과 비밀번호가 일치하지 않습니다. 다시 시도해주세요.</p>
{% endif %}

<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>

{% endblock %}

전반적인 블로그의 모양과 느낌을 기본 템플릿을 적용해 나타낼 수 있어요.

여기서 좋은 점은 just works[TM] 라는 거에요. 폼을 제출하거나 패스워드와 보완을 처리할 필요가 없어요. 단지 한 가지 남았다면, 우리는 mysite/settings.py 에 설정만 추가하면 됩니다. :

LOGIN_REDIRECT_URL = '/'

이제 바로 직접 로그인 하면, 최상 위 index 레벨에서 로그인이 성공적으로 될 거에요.

레이아웃 개선하기

지금까지는 인증된 유저만 포스팅을 추가/수정/발행할 수 있어요. 그런데 아직까지 미 인증된 유저도 추가/수정 버튼을 볼 수 있는데요. 이 노출을 숨겨보려 합니다. 템플릿을 수정이 필요하니, blog/templates/blog/base.html 파일부터 수정해봅시다.

<body>
    <div class="page-header">
        {% if user.is_authenticated %}
        <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
        <a href="{% url 'post_draft_list' %}" class="top-menu"><span class="glyphicon glyphicon-edit"></span></a>
        {% else %}
        <a href="{% url 'django.contrib.auth.views.login' %}" class="top-menu"><span class="glyphicon glyphicon-lock"></span></a>
        {% endif %}
        <h1><a href="{% url 'blog.views.post_list' %}">Django Girls</a></h1>
    </div>
    <div class="content">
        <div class="row">
            <div class="col-md-8">
            {% block content %}
            {% endblock %}
            </div>
        </div>
    </div>
</body>

어떤 패턴이 있음을 눈치채셨나요? 템플릿 내에서 if 문을 통해 인증 여부를 점검하고 있습니다. 인증이 되었을 시에는 추가/수정 버튼을 노출하고, 인증이 되지 않았을 시에는 로그인 버튼을 노출했습니다.

숙제: 인증된 유저에게만 수정 버튼만 노출되도록 blog/templates/blog/post_detail.html 파일을 수정해보세요.

인증된 사용자를 위한 기능 추가하기

템플릿에 달콤한 설탕을 추가해볼게요. 먼저 현재 우리가 로그인 중이라는 표시를 나타내주는 몇 가지 도구들을 추가할 것입니다. blog/templates/blog/base.html 를 아래와 같이 수정해봅시다. :

<div class="page-header">
    {% if user.is_authenticated %}
    <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
    <a href="{% url 'post_draft_list' %}" class="top-menu"><span class="glyphicon glyphicon-edit"></span></a>
    <p class="top-menu">Hello {{ user.username }}<small>(<a href="{% url 'django.contrib.auth.views.logout' %}">Log out</a>)</small></p>
    {% else %}
    <a href="{% url 'django.contrib.auth.views.login' %}" class="top-menu"><span class="glyphicon glyphicon-lock"></span></a>
    {% endif %}
    <h1><a href="{% url 'blog.views.post_list' %}">Django Girls</a></h1>
</div>

멋진 "안녕하세요. <이름>"구문을 추가함으로써 인증된 사용자라는 것을 알려줍니다. 또 블로그에서 로그아웃하는 링크를 추가해봅시다. 하지만 아직 작동하지 않네요. 이런, 우리가 인터넷을 망가뜨렸나 봐요. 자, 고쳐봅시다!

Django가 로그인의 처리를 하게 했듯이, Django가 로그아웃까지 되는지 살펴봅시다. https://docs.djangoproject.com/en/1.8/topics/auth/default/ 에서 관련 내용을 찾아봅시다.

다 살펴보았나요? django.contrib.auth.views.logout 뷰을 가리키는 Url (mysite/urls.py 안)을 추가해야 한다는 것을 알아챘을 거에요. 이렇게 말이에요.:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^accounts/login/$', 'django.contrib.auth.views.login'),
    url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}),
    url(r'', include('blog.urls')),
)

끝났어요! 여기까지 잘 따라왔다면(그리고 숙제를 했다면), 이런 기능을 갖춘 블로그가 만들어졌을 거에요.

  • 로그인을 위해 사용자 이름과 패스워드를 넣을 수 있습니다.
  • 포스트 생성/수정/게시(/삭제)하기위해 로그인을 할 수 있습니다.
  • 다시 로그아웃도 할 수 있습니다.