宿題: ウェブサイトをセキュアにする
管理画面を使用する以外で、パスワードを使用する必要がないことに気づいたかもしれません。これは、誰でもあなたのブログの投稿を追加したり編集したりできるということを意味するということにも気づいたかもしれませんね。私はあなたのことを存じ上げませんが、私は他の誰かが私のブログに投稿することは望んでいません。なので、対策をほどこしましょう。
投稿の追加/編集の承認
まずはセキュアにしてみましょう。post_new
, post_edit
, post_draft_list
, post_remove
, post_publish
のビューを保護し、ログインしているユーザだけがアクセスできるようにします。そうするために、Djangoには、デコレータ と呼ばれる素敵なヘルパーが同梱されています。今は技術的なことは気にしないでください。あとでフォローします。使いたいデコレータは、Djangoのdjango.contrib.auth.decorators
モジュールに同梱された、login_required
というものです。
それでは、blog/views.py
を編集し、一番上に次のimport文を追加してください:
from django.contrib.auth.decorators import login_required
次にpost_new
, post_edit
, post_draft_list
, post_remove
, post_publish
のそれぞれのビューの前に、以下のように1行追加してください(ビューにデコレータをつけています):
@login_required
def post_new(request):
[...]
以上です!http://127.0.0.1:8000/post/new/
にアクセスしてみましょう。違いに気づきましたか?
空のフォームが出てきたときは、管理画面のチャプターからログインしたままかもしれません。
http://127.0.0.1:8000/admin/logout/
にアクセスしてログアウトして、再度http://127.0.0.1:8000/post/new/
にアクセスしてみてください。
大切なエラーの一つが見えましたね。実際、これはとても興味深いです。追加したデコレータはログインページにリダイレクトするように動きますが、ログインページがまだ利用できないので、「Page not found (404)」が表示されています。
post_edit
, post_remove
, post_draft_list
, post_publish
にもデコレータを追加することを忘れないでくださいね。
やった!目標に一歩近づきました!!もう、他の人が私たちのブログに投稿することはできません。しかし残念ながら、私たちも投稿することができなくなってしまいました。次はそれを修正しましょう。
ユーザーログイン
ユーザーとパスワードと認証を実装するために多くの素敵なものを使ってみることもできますが、正しく使うのはやや複雑になります。Djangoは「電池同梱販売」(訳注:すべて揃っていることを表す)なので、ユーザーとパスワードと認証の実装という困難な仕事を誰かが成し遂げています。そこで、Djangoで提供されている認証ツールをさらに活用します。
mysite/urls.py
の中に、path('accounts/login/', views.LoginView.as_view(), name='login')
というurlを追加します。ファイルは次のようになるでしょう:
from django.urls import path, include
from django.contrib import admin
from django.contrib.auth import views
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/login/', views.LoginView.as_view(), name='login'),
path('', include('blog.urls')),
]
そして、ログインページのテンプレートが必要なので、blog/templates/registration
というディレクトリを作って、中にlogin.html
というファイルを作成してください:
{% extends "blog/base.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}
ブログの全体的な見た目を統一するために、基本 テンプレートを読み込んでいることがわかります。
いいところは、これだけで動作する ことです。フォーム送信の処理やパスワードの取り扱い、パスワードの保護は必要ありません。もう少し、やることが残っています。mysite/settings.py
に設定を追加します:
LOGIN_REDIRECT_URL = '/'
ログインページに直接アクセスして、ログインが成功したときにトップレベルのインデックス(ブログのホームページ)にリダイレクトします。
レイアウトを改善する
投稿を追加したり編集したりするためのボタンを認証されたユーザー(つまり、私たち)のみに見せるようにすでに設定しています。今度は、誰もがアクセスしたときにログインボタンを見せたいですね。
次のようにログインボタンを追加してみましょう:
<a href="{% url 'login' %}" class="top-menu"><span class="glyphicon glyphicon-lock"></span></a>
テンプレートを編集する必要があるので、blog/templates/blog/base.html
を開いて、<body>
と</body>
タグの中を次のように変更してください。
<body>
<div class="page-header">
{% if user.is_authenticated %}
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<a href="{% url 'post_draft_list' %}" class="top-menu"><span class="glyphicon glyphicon-edit"></span></a>
{% else %}
<a href="{% url 'login' %}" class="top-menu"><span class="glyphicon glyphicon-lock"></span></a>
{% endif %}
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
ここで、あるパターンがあることに気づくでしょう。if
条件式で、認証されたユーザーには追加/編集ボタンが表示されるようにしています。認証されていない場合は、ログインボタンが表示されます。
認証されたユーザーへの追加項目
ログイン状態のユーザーに向けて、テンプレートにもう少し付け加えましょう。まず、ログイン時に表示されるものを追加します。blog/templates/blog/base.html
を次のように修正します:
<div class="page-header">
{% if user.is_authenticated %}
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<a href="{% url 'post_draft_list' %}" class="top-menu"><span class="glyphicon glyphicon-edit"></span></a>
<p class="top-menu">Hello {{ user.username }} <small>(<a href="{% url 'logout' %}">Log out</a>)</small></p>
{% else %}
<a href="{% url 'login' %}" class="top-menu"><span class="glyphicon glyphicon-lock"></span></a>
{% endif %}
<h1><a href="/">Django Girls Blog</a></h1>
</div>
この追加によって、"Hello <username>" というふうに、何のユーザーでログインしているか、認証されているかがわかるようになります。また、ブログからログアウトするリンクを追加していますが、お気づきのとおり動いていません。次はそれを修正しましょう!
ログイン処理をDjangoに頼ったように、ログアウト処理も頼れるかどうか見てみましょう。https://docs.djangoproject.com/en/2.0/topics/auth/default/ をチェックしてみてください。
読みました?Djangoのログアウトビュー(例えばdjango.contrib.auth.views.logout
)を示すurlを次のようにmysite/urls.py
に追加することを思いついたんではないでしょうか:
from django.urls import path, include
from django.contrib import admin
from django.contrib.auth import views
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/login/', views.LoginView.as_view(), name='login'),
path('accounts/logout/', views.LogoutView.as_view(next_page='/'), name='logout'),
path('', include('blog.urls')),
]
以上です!これらのことをやり終えたら(そして宿題をやったら)、あなたのブログは次のようになっています。
- ログイン時にはユーザー名とパスワードが必須である
- 投稿を追加、編集、公開、削除時にはログインが必須である
- そして、ログアウトできる