wiki:DjangoHttpAuthMiddleware

Django用HTTP認証ミドルウェア

Djangoを使っている人はよくご存じのはずですが、Djangoにはユーザという概念が組み込まれています。 Djangoが提供する管理画面の利用だけでなく、僕らが作るDjangoアプリケーションでも、このDjango組み込みのユーザ機構を利用することができます。 当然ながら、この機構にはユーザ認証の仕組みが含まれています。 僕らは特に苦労することなく、数行の追加だけで、カッコいい(?)ログイン画面と、特定のユーザだけが特定のことをできるという権限管理の仕組みを手に入れることができます。

問題は、Djangoの提供するユーザ認証が、クッキーを用いた独自の方式だということです。 Djangoの認証を使ってしまうと、アプリケーション全体ないしサイト全体でDjangoの認証を使わなければいけない。 全体をゼロからDjangoだけで開発するならそれでいいのですが、誰かが作ったソフトウェアを組み合わせたり、既存システムと協調させようとするととたんに困ってしまいます。 せっかく、HTTPの規約には認証の仕組みが含まれているのに、これを使わないなんて…。 そりゃ、もちろん、いろいろ理由があってのことでしょうし、それは想像がつくのですが、ちょっと残念です。 「基本認証をそんなに嫌わないでよ…」なんて思います。

幸い、Djangoの認証の仕組みは拡張が可能です。 ここでは、「Djangoミドルウェア」という形で、HTTPの規約に基づいた認証を行うソフトウェアを作りました。 二つの異なる機能を含んでいます。

  1. HTTPサーバによる認証を信じる機能
  2. 自らHTTP認証を行う機能

ダウンロード

めんどくさいので、パッケージにはしてありません。 以下から「httpauth.py」をダウンロードして、適当な場所に置いてください。

HTTPサーバによる認証を信じる機能

「HTTPの規約」と言ってしまったら正確ではありませんが、DjangoをホストしているHTTPサーバによる認証を信じることです。 Apacheとか、LighttpdといったHTTPサーバ(ウェブサーバ)には、たいてい、ユーザ認証の仕組みが含まれています。 基本認証(Basic認証)とか、Digest認証といった、HTTPの規約で規定された認証をHTTPサーバ自体が行い、その結果を、昔懐かしのCGIとかPHPとか、あるいはDjangoといったウェブアプリケーションに教えてくれます。 この、HTTPサーバが教えてくれた認証結果を信じて、Djangoの認証に反映させることができます。 注意すべき点が2つあります。 HTTPサーバがユーザのパスワードなどを管理しユーザの真偽を判断しなきゃいけないこと。 さらに、HTTPサーバが申告してくるであろうユーザ名と同じユーザ名のユーザを、あらかじめDjangoのユーザデータベースに作成しておかなきゃいけないことです。 誰かに利用を許可したい場合に、DjangoのユーザデータベースとHTTPサーバとの二カ所に追加をしなきゃいけないのが面倒です。 当社では、自作の mod_auth_scriptっていうApache用認証モジュールを多用しているため、この面倒は無いのですけれど。

ちょっとしたコードの追加で、HTTPサーバが申告してきたユーザがユーザデータベースに無ければ作ってしまうという実装も可能ですが、ここでは敢えて、そうはしていません。

使うには、「settings.py」の「MIDDLEWARE_CLASSES」にミドルウェア名「httpauth.ByHttpServerMiddleware」を追加してください。 「django.contrib.auth.middleware.AuthenticationMiddleware」よりも前に挿入する必要があります。 あとは、Djangoでの認証のルール通り、「@login_required」などのデコレータをビュー関数につけるだけです。

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

自らHTTP認証を行う機能

Djangoによる基本認証の実装です。 基本認証をHTTPサーバにやらせるのではなく、Djangoのミドルウェアにやらせます。 この利点は、Djangoのユーザデータベースに記憶した認証情報を使って、ユーザのパスワードをチェックすることができることです。 前記の「HTTPサーバによる認証を信じる」場合と異なり、複数の場所にユーザを追加する必要がありません。 欠点は、現状では基本認証しか実装してないことです。

使うには、「settings.py」の「MIDDLEWARE_CLASSES」にミドルウェア名「httpauth.Middleware」を追加してください。 「django.contrib.auth.middleware.AuthenticationMiddleware」よりも前に挿入する必要があります。 あとは、認証を行いたいビュー関数に「@http_login_required()」デコレータをつけてください。

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

http_login_required」デコレータも、この「httpauth.py」に含まれています。 このデコレータは、本当を言うと「デコレータを返す関数」だということに注意してください。 省略できる引数「realm」には、基本認証の「レルム」文字列を指定することができます。 レルムを指定しない場合でも、「@http_login_required()」というように最後の「()」を忘れてはいけません。 あるいはこんなふうに、先にデコレータを作っておくのもいい手です。

private_area = http_login_required(realm='My Office')

@private_area
def view(request):
   ...

フォールバック

いずれのミドルウェアを使った場合も、これらミドルウェアで認証がなされなかった場合は、次のミドルウェアにフォールバックします。 次のと言っているのは、「settings.py」の「MIDDLEWARE_CLASSES」に記述したミドルウェアの順番でのことです。 フォールバックし、「django.contrib.auth.middleware.AuthenticationMiddleware」によるDjango標準の認証が行われます。

フォールバックが都合悪い場合…、ちょっとコードをいじれば実現できるはずですが、めんどうなのでやってません。

ご意見、感想など歓迎します。

(2007/12/28 - sgk)

mod_wsgi 上にての注意点

Apache の mod_wsgi で使用する際、httpd.conf に「WSGIPassAuthorization On」を設定する必要があります。