デジタル忍者ブログ

デジタル忍者ブログ

2019/05/13

Django2.0でログイン認証をカスタマイズしてみた

Django2.0の公式ドキュメントで確認すると、

ログイン認証で使われているUSERというモデルはデフォルトで使うべきでなく、

カスタムして利用することを勧めているようだ。


せっかくなので、AbstractBaseUserを使用して

カスタムを試みたら、いつの間にかログイン認証もカスタマイズしてしまった。


ログイン認証で使用するモデルをCustomUserとし、

USERで定義されている変数に加えて、住所情報等を加える。

もっと簡単な方法も存在するが、

AbstractBaseUserを使ったカスタマイズが自由度が高いため、

こちらを使用した。


ディレクトリ構造は以下の通り

(当記事の説明ため、必要最低限のみ記載)


project/

   app/

      models.py

      views.py

      auth.py

      ・・・

   project/

      settings.py

      ・・・


# project/app/model.py
from django.contrib.auth.models import AbstractBaseUser, UserManager

class CustomUser(AbstractBaseUser):
  userno = models.IntegerField(unique=True)
  username = models.CharField(max_length=30, unique=True)
  password = models.CharField(max_length=256)
  first_name = models.CharField(max_length=30, null=True, blank=True)
  last_name = models.CharField(max_length=30, null=True, blank=True)
  email = models.CharField(max_length=256, null=True, blank=True)
  postcd = models.CharField(max_length=8, null=True, blank=True)
  address1 = models.CharField(max_length=48, null=True, blank=True)
  address2 = models.CharField(max_length=48, null=True, blank=True)
  address3 = models.CharField(max_length=48, null=True, blank=True)
  telephone = models.CharField(max_length=15, null=True, blank=True)
  enabled = models.CharField(max_length=1)
  reguserno = models.IntegerField()
  regdate = models.DateTimeField()
  moduserno = models.IntegerField(null=True, blank=True)
  moddate = models.DateTimeField(null=True, blank=True)

  USERNAME_FIELD = 'username'

  objects = UserManager()
  
  class Meta:
    db_table = 'CustomUser'
    swappable = 'AUTH_USER_MODEL'


次にsettings.pyに以下を修正・追記する。

# project/project/settings.py

## 修正
INSTALLED_APPS = [
#    'django.contrib.admin', //コメントアウト

## 追記
AUTH_USER_MODEL = 'app.CustomUser'


AUTH_USER_MODELについては、ディレクトリ構造から、'app.models.CustomUser'が正しいと思っていたが、調べてみる限り、'app.CustomUser'が正しいみたいだ。


'django.contrib.admin'をコメントアウトにした理由は、

マイグレーション作成時に以下のエラーが発生したためである。

project> python manage.py makemigrations
SystemCheckError: System check identified some issues:

ERRORS:
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E019) The value of 'filter_horizontal[0]' refers to 'groups', which is not an attribute of 'app.CRI_M_USER'.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E019) The value of 'filter_horizontal[1]' refers to 'user_permissions', which is not an attribute of 'app.CRI_M_USER'.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E108) The value of 'list_display[4]' refers to 'is_staff', which is not a callable, an attribute of 'UserAdmin', or an attribute or method on 'app.CRI_M_USER'.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E116) The value of 'list_filter[0]' refers to 'is_staff', which does not refer to a Field.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E116) The value of 'list_filter[1]' refers to 'is_superuser', which does not refer to a Field.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E116) The value of 'list_filter[2]' refers to 'is_active', which does not refer to a Field.
<class 'django.contrib.auth.admin.UserAdmin'>: (admin.E116) The value of 'list_filter[3]' refers to 'groups', which does not refer to a Field.


つまり、CustomUserにはis_staffやis_activeの変数が存在しないことを示している。

本来はそれらも定義するべきであったが、より簡単な認証方法にしたかった。

そのため、'django.contrib.admin'をコメントアウトにした。

これにより、Djangoデフォルトで用意されている管理者サイトは使えない。


そうなると、ログイン認証もいつも通りの方法でエラーが発生しかねない可能性があったため、

ログイン認証もカスタマイズしようという判断に至った。


# project/app/views.py

from app.auth import *

def Logout(request):
  auth_logout(request)
  return redirect("app:login")


def Login(request):
  if request.method == 'POST':
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(request, username, password)
    if user is not None:
      return redirect("app:toppage")
    else:
      return render(request, 'login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'username': '', 'password': ''})
  else:
    return render(request, 'login.html', { 'msg': '', 'username': '', 'password': '' })
# project/app/auth.py

from django.contrib.auth import login, logout
from app.models import *
import hashlib

def authenticate(request, username=None, password=None):
  user = None
  try:
    user = CustomUser.objects.get(username=username)
  except CustomUser.DoesNotExist:
    return None
    
  if user.password == createPassword(password):
    login(request, user)
    return user
  return None


def createPassword(str):
  salt = '************'
  str = salt + str
  for i in range(1000):
    str = hashlib.sha256(str.encode('utf-8')).hexdigest()
  return str

def auth_logout(request):
  logout(request)
  return

以上の設定で、どうにかログイン処理を行うことができた。
この方法だと、ログイン認証の部分はマイクロサービスとして他の機能と分割し、
汎用化してもいいかもしれない。


Comment Form

コメント内容(必須)

Comment

現在、コメントはありません。