既存システムからDjangoへのリプレースで、ログイン機能をどう実装するかを検討して、実装した時のメモ。
0.事前検討
(前提)
- 既存システムDBには、既にID、パスワードソルト、パスワードハッシュ値がある。
- 既存ユーザーには、ログイン方法の変更の通知はできる。
(方針)
- この際、IDをメールアドレスに変更する。
- なるべく、Djangoのフレームに乗っ取る形で実装する。
- パスワードも再設定が可能なので、DjangoのUserモデル認証に切り替える。
- 将来は、メールベースの二段階認証にする。
(参考ページ)
1.【ログイン】LoginViewで実装(1)
ログイン部分については、LoginViewを使用して実装する。
基にしたVisual Studio のサンプルが、admin機能で利用しているので、利用者向けのログイン機能を追加する。
1-1)forms.py:ログインフォームの定義
forms.pyにユーザー用のログインフォームを定義する。Visual Studio のサンプルで既に定義されていたので、それをコピーして修正した。
変更点としては、usernameをメールアドレスで入力させるようにする。Userモデルにはusernameのほかにもemailも持っているがとりあえず。
class UserAuthenticationForm(AuthenticationForm): """Authentication for User Login.""" username = forms.CharField(max_length=254, widget=forms.EmailInput({ 'class': 'form-control', 'placeholder': 'Email アドレス'})) password = forms.CharField(label=_("Password"), widget=forms.PasswordInput({ 'class': 'form-control', 'placeholder':'パスワード'}))
基底のAutheticationFormについては、以下を参照した。
(GitHub)django.contrib.auth.forms.py
※今回は、従来のDBを使ったりとかしてカスタマイズすることも可能だとは思うが、手抜きで行くことに。
1-2)template:ログインページの定義
app/login.htmlというファイルで、テンプレートを作成。内容は、LoginViewを参考に。
1-3)url.pyの定義
urlpatternsに、ログインとログアウトを追加。
: from django.contrib.auth.views import LoginView, LogoutView from app import forms, views : urlpatterns = [ : path('login/', LoginView.as_view ( template_name='app/login.html', authentication_form=forms.UserAuthenticationForm, extra_context= { 'title': 'Login', }, ), name='login'), path('logout/', LogoutView.as_view(next_page='/'), name='logout'), : ] :
1-4)setting.pyの定義
ログイン後の最初のページを「LOGIN_REDIRECT_URL」で定義する。
2.【ログイン】LoginViewで実装(2)
(1)では、Userのusernameにメールアドレスを入れるという強引な方法にしたが、ユーザー名も使いたい場合は、認証でUser.emailをチェックするように修正する。
具体的には、認証バックエンドを拡張して、カスタマイズすればいい。
(GitHub)django.contrib.auth.backends.py
ベースのModelBackend.authenticateでは、以下のように取得している。
class ModelBackend(BaseBackend): : def authenticate(self, request, username=None, password=None, **kwargs): : user = UserModel._default_manager.get_by_natural_key(username) :
拡張クラスでは、DBのemailからusernameを検索するようにすればいいはず。とりあえずは、今回の運用は(1)の実装で問題ないので確認はしてないが、たぶん。
3.ログイン画面にお知らせ表示
ログイン画面の下に、DBから取得したお知らせ情報を表示するようにしたいので、修正する。
3-1)views.py:login関数の追加
1.ではLoginViewをurl.pyから直接呼び出すことで、views.pyの修正はなかったが、DBデータを取得するためにviews.pyに関数を追加する。
def login(request): """Renders the login page.""" assert isinstance(request, HttpRequest) # news list news_list = Information.news.order_by('-from_time') # DBからお知らせ情報を取得 view_obj = LoginView() view_obj.request = request view_obj.form_class = UserAuthenticationForm view_obj.template_name = 'app/login.html' view_obj.extra_context = { 'title':'Login', 'news_list':news_list, # 追加のお知らせ情報リスト } return view_obj.dispatch(request)
3-2)url.pyの定義
LoginViewを直接呼び出していたのを、login関数に変更。
: from django.contrib.auth.views import LogoutView from app import forms, views : urlpatterns = [ : path('login/', views.login, name='login'), # 変更部分 path('logout/', LogoutView.as_view(next_page='/'), name='logout'), : ] :
4.ログインが必要なページ
ログインが必要なページについては、以下でログインしていない場合、ログイン画面にリダイレクトさせる。
クラスビューを使っている場合は、MixInを使う。
5.二段階認証の追加
これは、別記事で後日。