読者です 読者をやめる 読者になる 読者になる

QueryDictをどうにかして拡張してハッシュ化したパスワードでログインしたかった話

パスワードのハッシュ化は基本

まあそりゃ平文で保存するはずないのであるユーザーがあるパスワードをつくったときはハッシュ化します
こんなmodels.pyがあるとする

from django.db import models

class User(models.Model):
    name = models.CharField(u"名前", max_length=64)
    password = models.CharField(max_length=64)
    is_active = models.BooleanField(default=True)
    ctime = models.DateTimeField(u'登録日時',auto_now_add=True, editable=False)
    atime = models.DateTimeField(u'更新日時',auto_now=True, editable=False)

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = 'User'

    @classmethod
    def register(cls, name, password):
        from schedule_proj.schedule.tools import Password
        user = cls.objects.create(name=name, password=Password.makeHash(password))
        return user

内部で使われているPasswordというのはこちら

import hashlib

class Password(object):

    @classmethod
    def makeHash(cls, password):
        return hashlib.sha1(password).hexdigest()

まあクラスにする必要もないかもしれないけど、要はhashlibを使ってhexdigestを記録しているだけ。

ここまでは簡単。問題はログイン

ログインするときに request.POST の内容で直接渡そうとするとこんな感じになってしまう

<QueryDict {'user':'hoge', 'password':'moge': 'csrftokenmiddleware':'hogehoge'}>

つまり request.POST.get('password') した値をつかってログインしようとしてもそれ自体はハッシュ化されてないために値が一致しない = ログインできないことになる

request.POST.copy()があった

結局こうした。もっとスマートな方法があるかもしれない
views.py

    if request.method == "POST":
        #リクエストのコピーをとる
        copy_req = request.POST.copy()
        #ハッシュ化して上書きする
        copy_req['password'] = Password.makeHash(request.POST.get('password'))
        #フォームを渡すときにパスワードを上書きしたリクエストを送る
        form = LoginForm(copy_req)
        if not form.is_valid():
            return HttpResponseRedirect(reverse('index'))
        #存在しないユーザーならindexに戻す
        if not User.objects.filter(name=form.cleaned_data['name']).count():
            return HttpResponseRedirect(reverse('index'))
        request.session['session_user'] = User.objects.get(name=form.cleaned_data['name'])

request.POST自体はQueryDictオブジェクトであり、このインスタンスに代入することはできない(はず)なのでこうした
参考::ueBLOG | request.POSTの挙動

Djangoのrequest.POSTはdjango.http.QueryDictオブジェクトのインスタンスです。 QuersyDictオブジェクトはdjango.utils.datastructures.MultiValueDictを継承してます。 ではMultiValueDictは何かというとpythonの辞書型を継承してます。

通常のPythonの辞書はkeyとvalueが一対一ですが、MultiValueDictは名前の通りkeyに対し複数のvalueを持ちます。 持ち方としては内部で配列で持ち、値を返すときに一番最後の配列の要素を返すようになっています。

ちなみにQueryDictはインスタンスを作ったときに入れた値を変更するとエラーになるように拡張したMultiValueDictです。 request.POSTで得たデータを変更したデータを使う場合にrequest.POST.copy()をしなくてはこのためです。

そんなわけで

無事にハッシュ化された値でログインできましたとさ。ちゃんちゃん。
もっとスマートなやり方はあるような気がするんだけどねー。Django付属のcontrib.authみたいなところをちょっと覗いたけどわからなかった。