=============================
장고의 사용자 인증
=============================
장고는 사용자 인증 시스템을 가지고 있습니다. 사용자 계정, 그룹,
허용권한(permission)과 쿠키를 기반으로 한 세션(session)을 지원합니다.
개요
========
인증 시스템은 다음으로 구성되어 있습니다.:
* 사용자
* 허용권한(permission): 작업 단위로 허용 여부를 바이너리(binary)로 구분
* 그룹: 하나 이상의 사용자에게 label과 허용권한(permission)을 부여
* 메세지: 사용자에게 메세지를 전달
설치
============
인증은 장고의 ``django.contrib.auth``에 포함되어 있습니다. 설치는
다음을 따르세요.:
1. ``'django.contrib.auth'``을 여러분의 settings.py의 ``INSTALLED_APPS``에
추가하세요.
2. ``manage.py syncdb``을 실행하세요.
참고로 ``'django.contrib.auth'``는 ``django-admin.py startproject``로
자동으로 만들어지는 ``settings.py``의 ``INSTALLED_APPS`` 항목에
포함되어 있습니다. ``INSTALLED_APPS``에 ``'django.contrib.auth'``가
이미 포함되어 있다면 ``manage.py syncdb``로 몇번이든 새로 인증시스템을
설치할 수 있습니다.
``syncdb`` 명령은 설치된 어플리케이션(installed apps)에 필요한
데이터베이스 테이블을 새로 만들고 처음 실행하는 경우에 관리자 계정을
추가로 만듭니다.
위 과정은 한번으로 충분합니다.
사용자
=====
사용자는 `django/contrib/auth/models.py`_에 구현된 장고 모델(Django
model)로 표현됩니다.
.. _django/contrib/auth/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py
API 참고
-------------
필드(field)
~~~~~~
``User`` 객체(object)는 다음과 같은 필드(field)를 가지고 있습니다.:
* ``username`` -- 반드시 필요. 최대 30자까지. 반드시 문자나 숫자,
'.'이나 '_' ``first_name`` -- 추가 선택. 최대 30자까지.
* ``last_name`` -- 추가 선택. 최대 30자까지.
* ``email`` -- 추가 선택. 이메일 주소.
* ``password`` -- 반드시 필요. 비밀번호를 해쉬(hash)로 저장합니다.
장고는 비밀번호를 읽을 수 있도록(raw password) 저장하지 않습니다.
비밀번호는 무한정 길어질 수 있고 어떤 글자도 포함될 수 있습니다.
아래 "Passwords" 항목을 읽어보세요.
* ``is_staff`` -- Boolean. 관리자 사이트에 접근할 수 있는 여부
* ``is_active`` -- Boolean. 로그인할 수 있는 여부. 사용자를
제거하는 대신 이 항목을 ``False``로 설정하세요.
* ``is_superuser`` -- Boolean. 모든 허용권한(permission)을
가지도록 합니다.
* ``last_login`` -- 사용자가 마지막으로 로그인할 날짜와
시간(datetime). 기본으로 현재 시간(date/time)이 저장됩니다.
* ``date_joined`` -- 사용자가 만들어진 날짜와 시간(datetime).
기본으로 현재 시간(date/time)이 저장됩니다.
메소드(method)
~~~~~~~
``User`` 객체(object)는 두 개의 ``many-to-many field``를 가집니다.:
``groups``과 ``user_permissions``. ``User`` 객체(object)는
다른 `Django model`_과 같은 방법으로 연관된 객체(object)에
접근할 수 있습니다.::
myuser.groups = [group_list]
myuser.groups.add(group, group,...)
myuser.groups.remove(group, group,...)
myuser.groups.clear()
myuser.user_permissions = [permission_list]
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...]
myuser.user_permissions.clear()
미리 정의된 이러한 메소드(method)와 함께, ``User`` 객체(object)는
다음과 같은 특별한 메소드(method)를 가지고 있습니다.
* ``is_anonymous()`` -- 항상 ``False``를 반환합니다. 이것으로
``User``와 ``AnonymousUser``를 구분할 수 있습니다. 일반적으로
``is_authenticated()``을 사용하는 것이 유리합니다.
* ``is_authenticated()`` -- 항상 ``True``를 반환합니다. 사용자가
인증을 통과했는지 말해줍니다. 단, 이 메소드(method)는 사용자의
허용권한(permission)이 아니라 사용자가 정확한 ``username``과
``password``로 인증을 통과했는지만 판단합니다.
* ``get_full_name()`` -- ``first_name``과 ``last_name``를
빈칸(space)으로 묶어서 가져옵니다.
* ``set_password(raw_password)`` -- 사용자 비밀번호를 지정합니다.
``User``객체(object)에 직접 비밀번호를 저장하지 마세요.
* ``check_password(raw_password)`` -- 주어진 사용자 비밀번호가
정확한지 검사합니다.
* ``set_unusable_password()`` -- **장고 개발버전에 새롭게 추가**
비밀번호를 사용하지 않도록 합니다. 단, 이것은 ``password``
필드(field)를 빈공백(blank string)으로 처리하는 것과는 다릅니다.
``password`` 필드(field)에 빈 공백(blank string)으로 저장하면
``check_password``는 ``True``를 반환하지 않습니다.
``User``객체(object)에 직접 저장하지 마세요.
이 방법은 LDAP 디렉토리 서비스와 같이 외부 인증시스템을 사용할
경우에 사용하세요.
* ``has_usable_password()`` -- **장고 개발버전에 새롭게 추가**
``set_unusable_password()``가 설정된 사용자는 ``False``를
반환합니다.
* ``get_group_permissions()`` -- 사용자가 속한 그룹의
허용권한(permission) 리스트(list)를 반환합니다.
* ``get_all_permissions()`` -- 사용자과 사용자의 그룹의
허용권한(permission) 리스트(list)를 반환합니다.
* ``has_perm(perm)`` -- 사용자가 ``"package.codename"``에 명시된
특정한 허용권한(permission)을 가지고 있는지 검사합니다.
``is_active``가 ``False``인 경우 항상 ``False``를 반환합니다.
* ``has_perms(perm_list)`` -- ``"package.codename"``에 명시된
허용권한(permission)들을 가지고 있는지 검사합니다.
``is_active``가 ``False``인 경우 항상 ``False``를 반환합니다.
* ``has_module_perms(package_name)`` -- 사용자가 특정한 장고
패키지(package)과 관련된 허용권한(permission)을 가지고 있는지
검사합니다. ``is_active``가 ``False``인 경우 항상 ``False``를
반환합니다.
* ``get_and_delete_messages()`` -- 사용자에게 전달될 ``Message``
객체(object) 리스트(list)를 큐(queue)에서 가져오고, 가져온 다음
큐(queue)에서 삭제합니다.
* ``email_user(subject, message, from_email=None)`` -- 사용자에게
메일 메세지를 보냅니다. ``from_email``항목이 ``None``일 경우에는
`DEFAULT_FROM_EMAIL`_ 설정이 사용됩니다.
* ``get_profile()`` -- 사용자의 사이트 프로필(site-specifi
profile)을 가져옵니다. 사이트의 프로필이 없을 때는
``django.contrib.auth.models.SiteProfileNotAvailable``
예외(Exception)가 발생합니다.
.. _Django model: ../model-api/
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
관리 함수(function)들
~~~~~~~~~~~~~~~~~
``User``모델(model)은 유용한 함수(function)들을 가지고 있습니다.
* ``create_user(username, email, password=None)`` -- 사용자를
만들고 ``User``객체(object)를 반환합니다. 만들어진 사용자는
``is_active=True``로 지정됩니다.
비밀번호가 주어지지 않는 경우에 ``set_unusable_password()``
메소드(method)가 호출됩니다.
_`사용자 만들기`에서 예제를 참고하세요.
* ``make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')``
주어진 길이와 글자들로 임의의 비밀번호를 만듭니다. (참고로
기본으로 지정된 ``allowed_chars``에는 ``1``, ``I`` and ``0``와
같은 햇갈리는 글자들은 제외되었습니다.)
기본적인 사용법
-----------
사용자 만들기
~~~~~~~~~~~~~~
일반적으로 ``create_user`` 함수(function)를 사용해서 사용자를
만듭니다.::
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# 여기서 사용자 객체(object)는 이미 데이터베이스에
# 저장되었습니다. 객체(object)의 속성(attribute)을 변경할 수
# 있습니다.
>>> user.is_staff = True
>>> user.save()
비밀번호 바꾸기
~~~~~~~~~~~~~~~~~~
``set_password()``로 비밀번호를 변경합니다.::
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username__exact='john')
>>> u.set_password('새로운 비밀번호')
>>> u.save()
특별한 이유가 없다면, 비밀번호를 ``User``객체(object)의 ``password``
속성(attribute)으로 지정하는 방법으로 변경하는 것은 바람직하지
않습니다. 이유는 다음 섹션에서 설명합니다.
비밀번호
---------
``User``객체(object)의 ``password``속성(attribute)은 다음과 같이 구성됩니다.::
hashtype$salt$hash
달러표시(dollar-sign)로 구분된 hashtype, salt 그리고 hash의 조합입니다.
Hashtype은 ``sha1`` (기본), ``md5`` 혹은 ``crypt`` 중 하나입니다. --
비밀번호를 거꾸로 풀 수 없는 알고리즘입니다. Salt는 hash를 만드는
비밀번호를 처리하는 임의의 글자(random string)들입니다. 참고로
``crypt`` 방법은 ``crypt`` 모듈을 포함하고 있는 표준 Python이 지원하는
플랫폼에서만 지원되고, ``crypt``는 장고 개발버전에서만 지원됩니다.
예로::
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
``User.set_password()``와 ``User.check_password()`` 메소드(method)가
이 값들을 처리할 수 있습니다.
0.90같은 이전 장고 버전에서는 간단하게 Salt 없이 MD5가 사용됩니다.
이전 버전을 위해서 이 방법도 여전히 유효합니다; 다만
``User.check_password()``가 실행되면 자동으로 새로운 형식으로
변경됩니다.
익명사용자
---------------
``django.contrib.auth.models.AnonymousUser``는
``django.contrib.auth.models.User``를 본받았지만 몇 가지 차이점이 있습니다.:
* ``id``는 항상 ``None``.
* ``is_staff``와 ``is_superuser``는 항상 ``False``.
* ``is_active``는 항상 ``True``.
* ``groups``와 ``user_permissions``는 항상 비어있습니다(empty).
* ``is_anonymous()``는 ``True``.
* ``is_authenticated()``는 ``False``.
* ``has_perm()``는 항상 ``False``.
* ``set_password()``, ``check_password()``, ``save()``,
``delete()``, ``set_groups()`` 그리고 ``set_permissions()``는
``NotImplementedError`` 예외(Exception)를 발생시킵니다.
실제로 ``AnonymousUser``를 필요 없을지도 모르지만, 다음 섹션에서
설명하는 Web requests에서 사용됩니다.
관리자 만들기
-------------------
settings.py의 ``INSTALLED_APPS``에 ``'django.contrib.auth'``를 추가한
상태에서 처음 ``manage.py syncdb``를 실행시키면 관리자 계정을
만듭니다. 나중에 관리자 계정을 따로 만들려면 ``create_superuser.py``를
사용할 수 있습니다.::
python /path/to/django/contrib/auth/create_superuser.py
``/path/to/``를 장고를 설치한 디렉토리로 바꿔주세요.
Web requests에서 인증하기
==============================
지금까지 이 문서는 인증과 관련된 객체(object)들을 다루는, 낮은
수준(low-level)에서 API를 설명했습니다. 장고는 인증
프레임워크(authentication framework)를 `request objects`_와
연결시킵니다.
먼저 장고의 미들웨어(middleware) 중에 ``SessionMiddleware``와
``AuthenticationMiddleware``를 settings.py의 ``MIDDLEWARE_CLASSES``에
추가합니다. 자세한 내용은 `session documentation`_를 참고하세요.
한번 이들 미들웨어(middleware)를 설치하면 뷰(views)에서
``request.user``를 접근할 수 있습니다. ``request.user``는 현재
로그인한 사용자를 표현한 ``User``객체(object)를 가리킵니다. 사용자가
로그인하지 않았다면, ``AnonymousUser``객체(object)를 대신 가리킵니다.
(이전 섹션을 참고하세요.) 다음과 같이 ``is_authenticated()``로 구분할
수 있습니다.::
if request.user.is_authenticated():
# 인증된 사용자의 경우,
else:
# 인증되지 않은 사용자의 경우,
.. _request objects: ../request_response/#httprequest-objects
.. _session documentation: ../sessions/
로그인시키는 방법은
--------------------
장고는 ``django.contrib.auth``에서 두가지 함수(function)를 제공합니다.:
``authenticate()``와 ``login()``.
사용자 이름과 패스워드로 인증하려면 ``authenticate()``
함수(function)를 사용하세요. 이 함수는 ``username``와 ``password``라는
두가지 키워드 인수(keyword argument)를 받아서 인증에 성공할 경우
``User``객체를 반환합니다. 그렇지 않으면 ``None``을 반환합니다. 예를
들면::
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
if user.is_active:
print "사용자이름과 비밀번호가 일치했습니다!"
else:
print "사용자 계정이 중단되었습니다!"
else:
print "주어진 사용자 이름과 비밀번호가 정확하지 않습니다."
뷰(view)에서 사용자 로그인을 처리하려면 ``HttpRequest``와 ``User``를
가지고 ``login()`` 메소드(method)를 사용하세요. ``login()``
메소드(method)는 장고 세션 프레임워크(session framework)를 통해서
세션(session)에 사용자 ID를 저장합니다. 물론 세션 미들웨어(session
middleware)를 설치했는지 확인해야합니다.
아래 ``authenticate()``와 ``login()``를 사용하는 예를 설명합니다.::
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# 인증된 사용자를 위한 페이지로 이동합니다.
else:
# '중지된 사용자'라는 메세지를 표시합니다.
else:
# Return an 'invalid login' error message.
# 로그인되지 않았다는 에러 메세지를 표시합니다.
비밀번호를 손수 검사하는 방법
-----------------------------------
``django.contrib.auth.models.check_password`` 함수(function)를
사용해서 글자로 표현된 비밀번호(plain-text password)와 데이터베이스에
hash로 저장된 비밀번호(hashed password)를 손수 비교해서 사용자를
인증할 수 있습니다. 두가지 인수(argument)가 필요합니다: 글자로 표현된
비밀번호와 데이터베이스에 저장된 hash처리된 비밀번호. 동일한 경우
``True``를 반환합니다. 그렇지 않을 경우에는 ``False``.
로그아웃시키는 방법
---------------------
``django.contrib.auth.login()``로 사용자를 로그아웃시키려면
뷰(view)에서 ``django.contrib.auth.logout()``를 사용하기 바랍니다.
``HttpRequest``가 주어져야 합니다. 예를 들면::
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# 로그아웃한 사용자를 위한 페이지로 이동합니다.
참고로 ``logout()``은 로그인하지 않은 사용자인 경우에도
예외(Exception)를 발생시키지 않습니다.
접근 제한하기
----------------------------------
낮은 수준에서 처리하는 방법
~~~~~~~~~~~
간단하게 ``request.user.is_authenticated()``로 검사해서 원하는 로그인
페이지로 이동시킬 수 있습니다.::
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...
...에러 메세지를 표시하도록 할 수도 있습니다.::
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...
로그인한 사용자를 체크하는 decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
간단하게 사용하기 위해서 ``login_required`` decorator를 사용할 수
있습니다.::
from django.contrib.auth.decorators import login_required
def my_view(request):
# ...
my_view = login_required(my_view)
Python 2.4부터 지원하는 decorator를 사용해서 좀더 간단하게 표시할 수
있습니다.::
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
# ...
장고 개발버전에서는 ``login_required`` decorator는
``redirect_field_name``이라는 키워드 인수(keyword argument)를 지정할
수 있습니다.::
from django.contrib.auth.decorators import login_required
def my_view(request):
# ...
my_view = login_required(redirect_field_name='redirect_to')(my_view)
Python 2.4에서 decorator를 사용한 좀더 간략한 표현을 사용할 수
있습니다.::
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='redirect_to')
def my_view(request):
# ...
``login_required``는 다음과 같은 역할을 합니다.:
* 로그인에 실패하면, ``settings.LOGIN_URL``으로
이동(redirect)합니다. 이때 이 URL에는 query string으로 ``next``를
이름으로 하는 ``redirect_field_name``을 값으로 함께 가지게
됩니다.
예를 들면:
``/accounts/login/?next=/polls/3/``.
* 사용자가 정상적으로 로그인하면, 뷰(view)가 실행됩니다.
뷰(view)의 코드는 사용자가 로그인했다고 가정합니다.
그리고 ``settings.LOGIN_URL``에 설정된 뷰(view)를 지정해야 합니다.
예를 들면 URLconf에 다음을 추가하세요.::
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
``django.contrib.auth.views.login``가 하는 역할은:
* ``GET`` 방식으로 호출하면 그냥 로그인 폼(form)을 보여줍니다.
* ``POST`` 방식으로 호출하면, 로그인을 시도합니다. 로그인에
성공하면 뷰(view)는 ``next``에 지정된 URL로 이동(redirect)합니다.
``next``가 지정되어 있지 않으면
``settings.LOGIN_REDIRECT_URL``로 이동합니다. (기본값은
``/accounts/profile/``) 로그인에 실패하면 다시 로그인 페이지로
이동합니다.
기본으로 정해진 ``registration/login.html``은 직접 구현해야 합니다. 이
템플릿(template)은 세가지 context 변수를 받습니다.
* ``form``: 로그인 폼(form)을 표현하는
``FormWrapper``객체(object). ``FormWrapper``에 대해서는
`forms documentation`_ 참고하세요.
* ``next``: 로그인에 성공한 뒤에 이동할 URL. query string도 포함할
수 있습니다.
* ``site_name``: 현재 ``SITE_ID``를 참조한 ``Site``의 이름. 장고
개발버전을 사용하고 사이트 프레임워크(site framework)를 사용하지
않는다면, ``request.META['SERVER_NAME']``의 값이 사용됩니다.
사이트(site)에 관해서는 `site framework docs`_를 참고하세요.
``registration/login.html`` 대신 템플릿(template)을 지정하고 싶으면,
URLconf를 설정할 때 뷰(view)에 ``template_name``을 인수(argument)로
지정할 수 있습니다. 예를 들면 ``myapp/login.html``을 사용하고 싶다면::
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
``registration/login.html`` 템플릿(template) 예입니다. 여기서는
``base.html`` 템플릿(template)을 content 블록(content block)으로
사용한다고 설정했습니다.::
{% extends "base.html" %}
{% block content %}
{% if form.has_errors %}
<p>사용자 이름과 비밀번호를 지정하세요.</p>
{% endif %}
<form method="post" action=".">
<table>
<tr><td><label for="id_username">사용자 이름:</label></td><td>{{ form.username }}</td></tr>
<tr><td><label for="id_password">비밀번호:</label></td><td>{{ form.password }}</td></tr>
</table>
<input type="submit" value="로그인" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}
.. _forms documentation: ../forms/
.. _site framework docs: ../sites/
이외에 다른 뷰(view)들
--------------------
``login`` 뷰(view) 이외에 인증 시스템에는 몇가지 뷰(view)들을 추가로
제공하고 있습니다.
``django.contrib.auth.views.logout``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
사용자를 로그아웃시킵니다.
**추가 인수(argument):**
* ``template_name``: 로그아웃시킨 다음 사용할 템플릿(template)의
전체 이름. 지정하지 않으면 ``registration/logged_out.html``이
기본으로 사용됩니다.
**템플릿(template) context:**
* ``title``: "Logged out"(언어설정에 따라 번역됩니다).
``django.contrib.auth.views.logout_then_login``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
로그아웃시킨 다음, 로그인 페이지로 이동(redirect)합니다.
**추가 인수(argument):**
* ``login_url``: 이동할 로그인 페이지의 URL. 지정하지 않으면
기본으로 ``settings.LOGIN_URL``을 사용합니다.
``django.contrib.auth.views.password_change``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
사용자가 패스워드를 변경할 수 있는 페이지를 제공합니다.
**추가 인수(argument):**
* ``template_name``: 패스워드를 변경할 수 있는 폼(form)을 포함한
템플릿(template) 이름을 지정합니다. 따로 지정하지 않으면
``registration/password_change_form.html``을 사용합니다.
**템플릿(template) context:**
* ``form``: 패스워드 변경에 필요한 폼(form).
``django.contrib.auth.views.password_change_done``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
사용자 패스워드를 변경한 다음 보게될 페이지.
**추가 인수(argument):**
* ``template_name``: 사용할 템플릿(template) 전체 이름. 지정하지
않으면 ``registration/password_change_done.html``을 사용합니다.
``django.contrib.auth.views.password_reset``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
사용자가 패스워드를 초기화할 수 있도록 합니다. 새로운 비밀번호는
장고에서 지정하고 새 비밀번호는 이메일로 전달합니다.
**추가 인수(argument):**
* ``template_name``: 사용할 템플릿(template) 전체 이름. 따로
지정하지 않으면 ``registration/password_reset_form.html``를
사용합니다.
* ``email_template_name``: 사용할 템플릿(template) 전체 이름. 따로
지정하지 않으면 ``registration/password_reset_email.html``를
사용합니다.
**템플릿(template) context:**
* ``form``: 패스워드 초기화에 사용될 폼(form).
``django.contrib.auth.views.password_reset_done``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
패스워드를 초기화하고나서 보여줄 페이지
**추가 인수(argument):**
* ``template_name``: 사용할 템플릿(template) 전체 이름. 따로
지정하지 않으면 ``registration/password_reset_done.html``를
사용합니다.
``django.contrib.auth.views.redirect_to_login``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**설명:**
로그인페이지로 이동(redirect)시킵니다. 로그인 이후 다시 다른 URL로
이동시킵니다.
**필요한 인수(argument):**
* ``next``: 로그인에 성공한 뒤에 이동(redirect)할 페이지 URL
**추가 인수(argument):**
* ``login_url``: 이동할 로그인 페이지의 URL. 따로 지정하지 않으면
``settings.LOGIN_URL``을 사용합니다.
기본으로 사용할 수 있는 폼(form)
---------------------
위에 설명한 인증과 관련된 뷰(view)를 사용하지 않으면서 장고가
제공하는 인증과 관련된 폼(form)을 그대로 사용하고 싶다면 아래 장고에서
제공하는 폼(form)들이 있습니다.:
* ``django.contrib.auth.forms.AdminPasswordChangeForm``: 관리자
화면에서 사용하는 사용자 비밀번호를 변경할 수 있는 폼(form)
* ``django.contrib.auth.forms.AuthenticationForm``: 사용자
로그인에 사용하는 폼(form)
* ``django.contrib.auth.forms.PasswordChangeForm``: 사용자가
패스워드를 변경할 수 있도록 하는 폼(form)
* ``django.contrib.auth.forms.PasswordResetForm``: 사용자 비밀번호
초기화에 사용하는 폼(form)
* ``django.contrib.auth.forms.UserCreationForm``: 새로운 사용자를
만드는 폼(form)
로그인된 사용자 접근 제한하기
---------------------------------------------------
허용권한(permission)에 의해서 접근을 제하는 방법은 이전 섹션에서
설명한 것과 다르지 않습니다.
간단하게 뷰(view)에서 ``request.user``를 가지고 검사할 수 있습니다.
예를 들어서 아래 뷰(view)는 사용자가 로그인되었는지,
``poll.can_vote``라는 허용권한(permission)을 가지고 있는지 검사합니다.::
def my_view(request):
if not (request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
return HttpResponse("투표할 수 없습니다.")
# ...
더 간단하게 ``user_passes_test`` decorator를 사용할 수 있습니다.::
from django.contrib.auth.decorators import user_passes_test
def my_view(request):
# ...
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
간단한 예를 들었습니다. 조금 뒤에 허용권한(permission) 여부만 검사할
수 있는 decorator를 말씀드립니다.
Python 2.4 이상에서는 decorator를 사용할 수 있습니다.::
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.has_perm('polls.can_vote'))
def my_view(request):
# ...
``user_passes_test``는 인수(argument)를 필요로 합니다: ``User``
객체(object)를 인수(argument)로 받아서 허용권한(permission)이 있으면
``True``를 반환하는 함수(function)입니다. ``user_passes_test``은
``User``객체(object)를 직접 검사하지 않습니다.
``user_passes_test()``는 또한 ``login_url``을 인수(argument)로
받습니다. 반환되는 결과에 따라서 이동할 로그인 페이지URL을 지정할 수
있습니다. 지정하지 않으면``settings.LOGIN_URL``이 사용됩니다.
Python 2.3에서는::
from django.contrib.auth.decorators import user_passes_test
def my_view(request):
# ...
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')(my_view)
Python 2.4에서는::
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')
def my_view(request):
# ...
permission_required decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
앞서 설명한 대로 ``permission_required()`` decorator는 사용자가 어떤
허용권한(permission)을 갖고 있는지 검사하는 기본적인 방법입니다.::
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote')(my_view)
``permission_required()``는 추가로 ``login_url``을 인수(argument)로
받습니다. 예를 들어서::
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
``login_required`` decorator처럼 ``login_url``도 따로 지정하지 않으면
``settings.LOGIN_URL``이 기본으로 사용됩니다.
일반적인 뷰(view)에서 접근 제한하기
--------------------------------
일반적인 뷰(view)(`generic view`_)에서 접근을 제한을 제한하려면
뷰(view)을 처리하는 함수(function)를 만들고 URLconf에 원래 뷰(view)
대신 함수(function)를 지정해줍니다. 예를 들어서::
from django.views.generic.date_based import object_detail
@login_required
def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs)
.. _generic view: ../generic_views/
허용권한(permission)
===========
장고는 간단한 허용권한(permission) 시스템을 갖고 있습니다. 사용자와
사용자의 그룹에 허용권한(permission)을 할당하는 방법을 제공합니다.
장고 관리자 사이트에서 주로 사용되지만, 여러분의 사이트에
적용해보세요.
장고 관리 사이트는 아래와 같이 허용권한(permission)을 사용합니다.
* 폼(form)을 통해서 새로운 객체(object)를 추가할 때 객체(object)
type에 대한 "add"(더하기) 허용권한(permission)으로 뷰(view)에
대한 사용자 접근을 제한합니다.
* 폼(form)을 통해서 객체(object)의 속성(attribute)을 변경할 때
객체(object) type에 대한 "change"(바꾸기)
허용권한(permission)으로 뷰(view)에 대한 사용자 접근을
제한합니다.
* 폼(form)을 통해서 객체(object)를 지울 때 객체(object) type에 대한
"delete"(지우기) 허용권한(permission)으로 뷰(view)에 대한 사용자
접근을 제한합니다.
허용권한(permission)은 특정한 객체(object)에 대해서가 아니라,
객체(object) 타입 별로 전역적(globally)으로 지정됩니다. 예를 들어서
장고에서는 이렇게 말할 수 있습니다. "영희는 새로운 기사를 바꿀 수
있습니다." 하지만 "영희는 자기가 쓴 새로운 기사만 바꿀 수 있습니다."
혹은 "영희는 특정한 기사 상태나 공개여부 혹은 기사 번호의 새로운
기사를 바꿀 수 있습니다."라는 식으로는 불가능합니다. 이 부분은 장고
개발자들끼리 얘기 중입니다.
허용권한(permission)의 기본
-------------------
세가지 기본 허용권한(permission) -- 더하기(add), 바꾸기(change),
지우기(delete) --는 ``class Admin``이 지정된 장고 모델(model)을 만들면
자동으로 부여됩니다. 좀 더 자세히 설명하자면, ``manage.py syncdb``가
실행되면 데이터베이스에는 ``auth_permission`` 테이블이 생성되는데 이
테이블에 해당 모델(model)에 대한 허용권한(permission)이 저장됩니다.
물론 ``class Admin``을 모델(model)에 지정하지 않은 채로 ``syncdb``를
실행하면 허용권한(permission)은 부여되지 않습니다. ``class Admin``를
모델(model)에 추가하고 다시 ``manage.py syncdb``를 실행하세요. 그러면
빠졌던 허용권한(permission)이 새로 부여됩니다.
개발자가 정의한 허용권한(permission)
------------------
`model Meta attribute`_에 ``permissions``을 추가해서 개발자가 직접
정의한 허용권한(permission)을 사용할 수 있습니다.
아래 모델(model)은 세가지 허용권한(permission)을 만들었습니다.::
class USCitizen(models.Model):
# ...
class Meta:
permissions = (
("can_drive", "Can drive"),
("can_vote", "Can vote in elections"),
("can_drink", "Can drink alcohol"),
)
이것도 또한 ``syncdb``를 할 때 추가로 지정된 허용권한(permission)으로
추가됩니다.
.. _model Meta attribute: ../model-api/#meta-options
API 참고
-------------
사용자 클래스(class)처럼 허용권한(permission)은
`django/contrib/auth/models.py`_이 정의하는 장고 모델(model)에서
구현되어 있습니다.
.. _django/contrib/auth/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py
필드(field)
~~~~~~
``Permission``객체(object)는 아래와 같은 필드(field)를 가지고 있습니다.:
* ``name`` -- 반드시 필요. 최대 50자까지. 예: ``'Can vote'``.
* ``content_type`` -- 반드시 필요. ``django_content_type``
테이블이 정의에 따릅니다.
* ``codename`` -- 반드시 필요. 최대 100자까지. 예: ``'can_vote'``.
메소드(method)
~~~~~~~
``Permission``객체(object)는 다른 `Django model`_에서처럼 데이터에
접근하는 일관된 메소드(method)를 제공합니다.
템플릿(template)에서 인증과 관련된 데이터
================================
``RequestContext``를 사용할 경우 현재 로그인된 사용자와 이 사용자에
부여된 허용권한(permission)은 `template context`_를 통해서 접근할 수
있습니다.
.. admonition:: 좀더 자세히
이 변수들은 설정(settings) 중 ``TEMPLATE_CONTEXT_PROCESSORS``에
``"django.core.context_processors.auth"``를 추가하고
``RequestContext``를 사용할 경우에만 가능합니다. 자세한 내용은
`RequestContext docs`_를 참고하세요.
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
사용자
-----
``User``객체(object) 혹은 ``AnonymousUser``객체(object) 모두
템플릿(template) 변수, ``{{ user }}``에 저장됩니다.::
{% if user.is_authenticated %}
<p>안녕하세요, {{ user.username }}. 로그인하셨습니다.</p>
{% else %}
<p>안녕하세요, 로그인하세요.</p>
{% endif %}
허용권한(permission)
-----------
로그인된 사용자의 허용권한(permission)은 템플릿(template) 변수,
``{{ perms }}``에 저장됩니다. ``{{ perms }}``은
``django.core.context_processors.PermWrapper``의 객체(object)입니다.
``{{ perms }}``은 ``User.has_module_perms``을 직접 가리킵니다.
즉, 어플리케이션(app) 이름으로 허용권한(permission)을 객체(object)
속성(attribute)으로 가져올 수 있습니다.::
{{ perms.foo }}
``User.has_perms``를 통해서 접근 여부를 허용권한(permission) 이름으로
객체(object) 속성(attribute)으로 가져올 수 있습니다.::
{{ perms.foo.can_vote }}
다음과 같이 템플릿(template)의 ``{% if %}`` 구문으로
허용권한(permission)을 검사할 수 있습니다.::
{% if perms.foo %}
<p>foo 어플리케이션에 접근할 수 있습니다.</p>
{% if perms.foo.can_vote %}
<p>투표할 수 있습니다!</p>
{% endif %}
{% if perms.foo.can_drive %}
<p>운전할 수 있습니다!</p>
{% endif %}
{% else %}
<p>foo 어플리케이션에서 할 수 있는 게 없군요.</p>
{% endif %}
.. _template context: ../templates_python/
그룹
======
그룹은 사용자를 분류하는 일반적인 방법입니다. 그룹으로 사용자들을
묶어서 허용권한(permission)을 적용하고 이름을 부여할 수 있습니다.
하나의 사용자는 여러 그룹에 속할 수 있습니다.
그룹에 포함된 사용자는 자동으로 그 그룹의 허용권한(permission)을
가지게 됩니다. 예를 들어서 ``Site editors``라는 그룹은
``can_edit_home_page``라는 허용권한(permission)을 가지고 있고 이
그룹에 속한 사용자들도 ``can_edit_home_page`` 허용권한(permission)을
부여받게 됩니다.
허용권한(permission) 이외에 그룹은 사용자들을 쉽게 분류해서 관리할 수
있도록 해줍니다. 예를 들어서 ``'Special users'``라는 그룹을 만들고
사이트에 이 그룹에 속한 사용자들만 접근할 수 있는 페이지를 제공하거나
이 그룹에 속한 사용자들 모두에게 메일을 보낼 수도 있습니다.
메세지
========
메세지 시스템은 사용자들에게 쉽게 메세지를 보낼 수 있는 큐(queue)
형태의 시스템입니다. (역자주: 장고에서 얘기하는 메세지 시스템은 메일을
발송을 의미하지 않습니다. 장고의 메세지 시스템은 페이지에 간단한 작업
결과 메세지를 표시하는 데 주로 사용됩니다. 아래 예제를 참고하세요.)
메세지는 ``User`` 객체(object)와 묶여 있습니다. 단 메세지를 보낸
시간은 기록하지 않습니다.
주로 메세지는 장고 관리 페이지에서 어떤 작업을 끝냈을 때 사용됩니다.
예를 들어서 ``"The poll Foo was created successfully."``가
메세지입니다.
API는 간단합니다.:
* ``user_obj.message_set.create(message='message_text')``로 새
메세지를 만들 수 있습니다.
* ``user_obj.get_and_delete_messages()``는 이
사용자에게 전달된 ``Message``를 리스트(list) 형태로 반환합니다.
아래 예에서, playlist를 만든 다음 사용자에게 메세지를 남깁니다.::
def create_playlist(request, songs):
# 몇몇 노래로 노래 목록을 만듭니다.
# ...
request.user.message_set.create(message="노래 목록을 잘 만들었습니다.")
return render_to_response("playlists/create.html",
context_instance=RequestContext(request))
``RequestContext``을 사용할 수 있는 상태에서 로그인한 사용자의
메세지는 `template context`_의 템플릿(template) 변수로 가져올 수
있습니다.::
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
``RequestContext``는 메세지가 보이는 것과 상관 없이
``get_and_delete_messages``를 호출해서 사용자에게 할당된 메세지를
지웁니다.
장고 메세지 시스템은 데이터베이스에 등록된 사용자에게만 사용할 수
있습니다. 등록되지 않은 익명(anonymous) 사용자에게도 적용하고 싶다면
`session framework`_를 활용하는 방법이 있습니다.
.. _session framework: ../sessions/
이외 인증과 관련된 소스
============================
장고의 인증 시스템은 대체로 일반적인 요구사항을 만족시키고 있습니다.
하지만, 다른 인증시스템과 연동하는 경우도 있을 수 있습니다.
예를 들어서, 여러분의 회사가 직원 계정을 LDAP로 관리하고 있는 경우에는
장고 인증시스템과 LDAP에서 각각 계정을 관리하기는 어려울 것입니다.
그래서 이런 상황을 해결하기 위해서 장고의 인증시스템은 다른
인증시스템을 이용할 수 있는 방법은 제공합니다. 장고의 기본
데이터베이스 구조를 다시 작성하거나 다른 인증시스템과 함께 사용할 수
있습니다.
인증 저장소(authentication backends)
----------------------------------
장고는 내부적으로 "인증 저장소"의 목록을 가지고 있습니다.
``django.contrib.auth.authenticate()``가 호출되면 -- "로그인시키는
방법은"에서 설명한 것처럼 -- 모든 인증 저장소를 사용해서 인증을
시도합니다. 첫번째 저장소(backend)에서 실패하면 다음 번
저장소(backend)로 시도하는 식으로 모든 저장소(backend)를 사용합니다.
인증 저장소(backend) 목록은 ``AUTHENTICATION_BACKENDS`` 설정에
튜플(tuple) 형태로 지정합니다.
기본적으로, ``AUTHENTICATION_BACKENDS``는::
('django.contrib.auth.backends.ModelBackend',)
이것은 장고 사용자 데이터베이스를 다루는 기본 인증
저장소(backend)입니다.
``AUTHENTICATION_BACKENDS``에서는 순서가 중요합니다. 여러
저장소(backend)에 같은 사용자와 비밀번호가 있는 경우에는, 먼저 인증에
성공하는 저장소(backend)가 사용됩니다.
인증 저장소(authentication backend) 작성하기
---------------------------------
인증 저장소(backend)는 두가지 메소드(method)만 구현하면
됩니다:``get_user(user_id)``, ``authenticate(**credentials)``
``get_user`` 메소드(method)는 ``user_id``를 인수(argument)도 받아서
``User``를 반환합니다. -- 저장소(backend)에 저장된 사용자 이름입니다.
``authenticate`` 메소드(method)는 키워드 인수(keyword argument)를
받습니다. 대부분 이런 형태를 보입니다.::
class MyBackend:
def authenticate(self, username=None, password=None):
# username과 password를 검사해서 ``User``를 반환합니다.
다음처럼 token을 받기도 합니다.::
class MyBackend:
def authenticate(self, token=None):
# token을 검사해서 ``User``를 반환합니다.
``authenticate`` 메소드(method)는 인증조건(credentials)을 검사해서
인증을 통과하면 ``User``를, 아니면 ``None``을 반환합니다.
장고 관리 시스템은 이 문서 처음에서 설명한 것처럼 ``User``
객체(object)와 밀접하게 물려있습니다. 따라서 다른 인증시스템(예를
들어서, LDAP 디렉토리 서비스, 혹은 외부 SQL 데이터베이스)와 연동하기
위해서 가장 좋은 방법은 사용자의 장고의 ``User``객체(object)를 만드는
것입니다. 따라서 따로 스크립트를 작성하거나 사용자가 로그인할 때
``authenticate`` 메소드(method)를 호출해줄 수도 있습니다.
``settings.py``에 정의된 사용자와 패스워드로 인증하는 예입니다.::
from django.conf import settings
from django.contrib.auth.models import User, check_password
class SettingsBackend:
"""
ADMIN_LOGIN와 ADMIN_PASSWORD 설정으로 인증을 시도합니다.
로그인 이름과 hash처리된 비밀번호를 사용합니다. 예를 들어:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
"""
def authenticate(self, username=None, password=None):
login_valid = (settings.ADMIN_LOGIN == username)
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# 새로운 사용자를 만듭니다. settings.py에 정의된
# 패스워드를 그대로 사용합니다.
user = User(username=username, password='get from settings.py')
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
다른 인증 저장소(custom backends) 다루기
-----------------------------------------
다른 인증 저장소(backend)는 나름대로의 허용권한(permission)을 제공할
수 있습니다.
user 모델(model)의 허용권한(permission)에 접근하는
함수(function)들(``get_group_permissions()``,
``get_all_permissions()``, ``has_perm()``, 그리고
``has_module_perms()``)은 인증 저장소(backend)가 구현한
함수(function)에 의존합니다.
사용자에게 부여된 허용권한(permission)은 모든 저장소(backend)가 가진
모든 허용권한(permission)의 일부분입니다. 즉, 사용자는 어떤 인증
저장소(backend)가 허용한다면 허용권한(permission)을 가질 수 있습니다.
간단한 저장소(backend)를 구현한 예입니다. 관리자의
허용권한(permission)을 검사합니다.::
class SettingsBackend:
# ...
def has_perm(self, user_obj, perm):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
위 예는 관리자에게 모든 허용권한(permission)을 허용합니다. 인증
저장소(backend)의 함수(function)들은 ``User``객체(object)와 ``User``
메소드(method)가 받은 인수(argument)를 함께 전달받습니다.
인증에 관해서는 ``auth_permission`` 테이블을 사용하는 기본 인증
저장소(backend)인 ``django/contrib/auth/backends.py``를 참고하세요.
.. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py