認証処理

djangoで認証処理を実装してみました。(DjangoでTodoアプリケーションの作成の続きとして)

認証のための設定

settings.pyのアプリケーションリストにdjango.contribut.authが含まれている必要があります。(デフォルトで含まれている)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'djsite.todo',

)

settings.pyのミドルウェアリストにAuthenticationMiddlewareが含まれている必要があります。(デフォルトで含まれている)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

データベースのインストール

上記の設定がされている状態で、管理スクリプトでsyncdb(データベース同期)を行うと認証関連のデータベーステーブルの生成、初期化が行われます。

./manager.py syncdb

認証フォーム

認証ページの表示に対応するフォームを定義しました。

from django import  forms
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login

..... ....

class LoginForm(forms.Form):
	username = forms.CharField(label="ユーザー名", required=True, max_length=30)
	password = forms.CharField(label="パスワード", required=True, max_length=30,
	 	widget=forms.PasswordInput)

	def clean_auth(self):
		if not self.is_valid():
			return self.cleaned_data
		user = authenticate(username=self.cleaned_data['username'],
		 	password=self.cleaned_data['password'])
		if user is not None:
			if user.is_active:
				self.cleaned_data['user'] = user
				return self.cleaned_data
			else:
				raise forms.ValidationError('Authorication error.')
		else:
			raise forms.ValidationError('Authorication error.')		

	def clean(self):
		cleaned = forms.Form.clean(self)
		self.clean_auth()
		return cleaned
    1. フォームのclean()メソッドをオーバーライドして認証に失敗したときにValidationErrorが投げられるようにしています。フォームのvalidationが走ったときこの例外が投げられるとにエラーリスト(errors)にエラーが追加されるようになります。
    2. 認証自体は、django.contrib.authのauthenticate関数で行っています。これでインストールされているユーザテーブル(auth_user)を検索して認証が行われます。

認証ページの表示と認証の実行

ビュー(views.py)に認証ページの表示と認証を行う関数を追加します。

from django.http import HttpResponse,HttpResponseRedirect
from django.shortcuts import render_to_response

from djsite.todo.forms import  LoginForm

def show_login(request):

	if request.POST:
 	    form = LoginForm(request.POST)
        next = request.POST.get('next', None)
		next = request.GET.get('next', None)
		if not form.is_valid():
			return render_to_response('registration/login.html',
				dict(form=form, next=next))
		cleaned = form.clean()
		login(request, cleaned['user'])
		return HttpResponseRedirect(next)
	else:
    	next = request.POST.get('next', None)
		form = LoginForm()
		return render_to_response('registration/login.html',
			dict(form=form, next=next))
    1. POSTであれば、認証を実行、GETであれば認証ページの表示を行います。
    2. 認証成功後に遷移するページのパスがリクエストパラメータでnext受け渡していきます。
    3. 認証が成功すれば、django.contrib.authのlogin関数で認証情報をセッションに登録、次のページに遷移します。

認証ページへのリダイレクト

認証が必要なViewの場合、認証ページへリダイレクトされるようにします。

from django.http import HttpResponse,HttpResponseRedirect
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response

from djsite.todo.forms import LoginForm

....

@login_required
def show_list(request):
	"""docstring for show_list"""
	user = request.user
    .....  ...
    1. デコレーターとして、django.contrib.auth.decoratorsのlogin_requiredを指定します。これにより、認証を確認して、認証されていなければ認証ページ(URLパターン「accounts/login/」で指定されるView)へリダイレクトされます。
    2. requeseのuser属性として、認証情報が参照できます。

URLパターンの追加

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('djsite.todo.views',
	(r'^list$','show_list'),
	(r'^accounts/login/','show_login'),
	(r'^accounts/logout/','logout'),
)
    1. 「^accounts/login/」というパターンが認証ViewへのUrlとなるので、そのパターンに対する関数を指定します。

テンプレート

ユーザ認証ページのテンプレートです。作成した認証フォーム(LoginForm)の属性と次に遷移するurl値をフォーム要素として含みます。

<head>
</head>
<body>
<h1>Login</h1>
{%if form.errors %}
<p style="color: red;">
	認証に失敗しました。
</p>
{% endif %}

<form action="/accounts/login/?next={{ request.GET.next }}"
      method="post">
  <table>
    <tr>
	<td>
    <label class="fortextinput" for="id\_username">
      Username:
    </label>
	</td>
	<td>
    {{ form.username }}
    {%if form.username.errors %}
      <span style="color: red;">
      {{ form.username.errors|join:", " }}
      </span>
    {% endif %}
	</td>
    </tr>
	<tr>
	<td>
    <label class="fortextinput" for="id\_password">
      Password:
    </label>
	<td>
    {{ form.password }}
    {%if form.password.errors %}
      <span style="color: red;">
      {{ form.password.errors|join:", " }}
      </span>
    {% endif %}
	</td>
    </tr>
  </table>
  <input type="hidden" name="next" value="{{next}}"/>
  <input type="submit" value="Login"/>

</form>
</body>