前置き
便利なはずの python の Web フレームワークが、非常に理解しにくいものになっています。
ここでは、なるべくシンプルに最低限の機能だけを使用して Django の認証ページを制作します。
ユーザの登録・削除は shell_plus を使用してコンソールから操作します。
専用の Web ページはここでは作成しません。
テンプレートのネスト (include) や条件分岐マクロ、定番の Bootstrap 等を導入していないのは、わざとです。
最初から使うと Django を理解しにくくなってしまいます。
それらが今すぐ必要な人は、そもそも本稿を読む必要がないと思いますが···。
サーバ
Django のインストールがまだなら、インストールしておいてください。
項目 内容 備考 OS CentOS Linux release 8.4.2105 CentOS 8 のサポートも 2021 年限り。方針は突然変更しないでほしかった。 IP アドレス:HTTP ポート 192.168.11.7:8000 - 使用言語 Python 3.6.8 - Python パッケージマネージャ pip 21.2.4 - フレームワーク Django 3.2.6 - データベース SQLite 3.26.0 Django に付属のもの。
設置場所
(*1)アプリケーション名の先頭は小文字にする必要がある。
項目 内容 URL 備考 設置ディレクトリ /home/who/ - ユーザ名 who のホームディレクトリに設置する。
本稿説明では ~/ がホームディレクトリの意味。プロジェクト名 hello - - アプリケーション アカウント accounts(*1) http://192.168.11.7:8000/accounts/login/ ログイン・ログアウトページは
LoginView, LogoutView で実装する。http://192.168.11.7:8000/accounts/logout/ コンテンツ world(*1) http://192.168.11.7:8000/ 通常コンテンツを表示する。 http://192.168.11.7:8000/world/数値(*2)/ 認証コンテンツを表示する。 http://192.168.11.7:8000/world/数値(*2)/edit/ 認証コンテンツを編集する。(今回はダミー)
(*2)「数値」はサンプルのためのダミー。本稿では数値が変化しても同じページを表示する。
認証用メンバ
項目 ユーザ ID メールアドレス パスワード 備考 管理ユーザ admin admin@example.jp adminpassword - 通常ユーザ user1 user1@example.jp password -
本稿は大きく分けて以下の構成になっている。上から順に実施すること。 項 1~3 ··· プロジェクトの作成 項 4~6 ··· 通常コンテンツの設置 項 7~11 ··· 認証機能の追加
1-1. OS のバージョンを確認する。
~/$ cat /etc/system-release1-2. Python と pip のバージョンを確認する。
CentOS Linux release 8.4.2105
~/$ python3 --version1-3. Django のバージョンを確認する。
~/$ pip --version
Python 3.6.8
pip 21.2.4 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)
~/$ python3 -m django --version
3.2.6
2-1. django-admin コマンドの確認をする。
~/$ which django-admin2-2. プロジェクトを作成する。
/usr/local/bin/django-admin
~/$ django-admin startproject hello2-3. 作成したプロジェクトのディレクトリツリーを確認する。
~/$ mkdir ~/hello/templates/
~/$ LANG=c tree ~/hello/2-4. 作成したプロジェクトの初期設定をする。
/home/who/hello/ |-- hello | |-- __init__.py | |-- asgi.py | |-- settings.py | |-- urls.py | `-- wsgi.py |-- manage.py `-- templates
~/$ cd ~/hello/2-5. shell_plus の動作確認をする。
~/hello/$ vim ~/hello/hello/settings.py
(省略) ALLOWED_HOSTS = ['*'] # 全インタフェースからのリクエストを受け付ける。 (省略) INSTALLED_APPS = [ (省略) 'django_extensions', # manage.py の shell_plus を使用可能にする。 ] (省略) TIME_ZONE = 'Asia/Tokyo' # タイムゾーンを日本にする。(内部形式は変化しない。(UTC のまま) (省略)
~/hello/$ python3 ./manage.py shell_plus -c "for item in 'SHELL_PLUS_PRE_IMPORTS','SHELL_PLUS_IMPORTS','SHELL_PLUS_POST_IMPORTS': print(item + ' ' + str(getattr(settings, item, {})))"2-6. プロジェクトの動作確認をする。shell_plus でインポートされるモジュールを確認。
# Shell Plus Model Imports from django.contrib.admin.models import LogEntry from django.contrib.auth.models import Group, Permission, User from django.contrib.contenttypes.models import ContentType from django.contrib.sessions.models import Session # Shell Plus Django Imports from django.core.cache import cache from django.conf import settings from django.contrib.auth import get_user_model from django.db import transaction from django.db.models import Avg, Case, Count, F, Max, Min, Prefetch, Q, Sum, When from django.utils import timezone from django.urls import reverse from django.db.models import Exists, OuterRef, Subquery SHELL_PLUS_PRE_IMPORTS {} SHELL_PLUS_IMPORTS {} SHELL_PLUS_POST_IMPORTS {}
2-6-1. Django 付属のテストサーバ機能を起動する。
~/hello/$ python3 ./manage.py runserver 0:80002-6-2. ブラウザを起動し、テストサーバにアクセスする。
「0:8000」は、当該サーバの全 IP アドレスのポート 8000 番で待ち受ける指定。
停止する場合は Ctrl - C を入力する。
「適用されていない移行がある」と警告されている。
Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. December 18, 2021 - 19:22:29 Django version 3.2.6, using settings 'hello.settings' Starting development server at http://0:8000/ Quit the server with CONTROL-C.
これは、後述「3. データベースの初期化」を実施すると警告されなくなる。
それまで admin, auth, contenttypes, sessions は使用できない。
Windows10 の PowerShell から Firefox を起動する例2-6-3. テストサーバ実行後のプロジェクトのディレクトリツリーを確認する。
>_ Windows PowerShell
Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. 新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000 ⏎ PS C:\Users\who>
Firefox の表示
~/hello/$ LANG=c tree ~/hello/
(*3)db.sqlite3 は Django によって作られる。
/home/who/hello/ |-- db.sqlite3 (runserver の実行時に生成された。まだ空っぽ)(*3) |-- hello | |-- __init__.py | |-- __pycache__ (runserver の実行時に生成された)(*4) | | |-- __init__.cpython-36.pyc ( 〃 ) | | |-- settings.cpython-36.pyc ( 〃 ) | | |-- urls.cpython-36.pyc ( 〃 ) | | `-- wsgi.cpython-36.pyc ( 〃 ) | |-- asgi.py | |-- settings.py | |-- urls.py | `-- wsgi.py |-- manage.py `-- templates
(*4)__pycache__ (と *.pyc) が作られるのは Python が持っている機能による。
今回使用する Django 付属のログイン機能はデータベースが必要。
データベースが無い状態でログイン機能を使用すると、エラー表示され動作しない。
エラー表示の例:3-1. データベースのテーブル (スキーマ) を確認する。
django.db.utils.OperationalError: no such table: django_session
~/hello/$ sqlite3 ./db.sqlite3 .table3-2. データベースのテーブルを作成する。
(何も表示されない)
~/hello/$ python3 ./manage.py makemigrations3-3. データベースのテーブルを確認する。
~/hello/$ python3 ./manage.py migrate最初なので、以前の変更は何もない。
No changes detected
Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK
~/hello/$ sqlite3 ./db.sqlite3 .table
auth_group auth_user_user_permissions auth_group_permissions django_admin_log auth_permission django_content_type auth_user django_migrations auth_user_groups django_session
4-1. Django アプリケーション world を追加する。
4-1-1. アプリケーションを追加する。4-2. 通常コンテンツの作成
~/hello/$ python3 ./manage.py startapp world4-1-2. アプリケーションをプロジェクトに追加する。
~/hello/$ LANG=c tree ~/hello/
/home/who/hello/ |-- db.sqlite3 |-- hello (省略) |-- manage.py |-- templates `-- world (startapp world の実行時に生成された) |-- __init__.py ( 〃 ) |-- admin.py ( 〃 ) |-- apps.py ( 〃 ) |-- migrations ( 〃 ) | `-- __init__.py ( 〃 ) |-- models.py ( 〃 ) |-- tests.py ( 〃 ) `-- views.py ( 〃 )
~/hello/$ cat ~/hello/world/apps.py
~/hello/$ vim ~/hello/hello/settings.py
from django.apps import AppConfig class WorldConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'world'
(省略) INSTALLED_APPS = [ (省略) 'django_extensions', 'world.apps.WorldConfig', # ← 追加 ] (省略)
4-2-1. テンプレートファイルを作成する。
~/hello/$ mkdir -p ~/hello/world/templates/world/4-2-2. ディスパッチャを設定する。
~/hello/$ echo 'Hello world' > ~/hello/world/templates/world/top.html
~/hello/$ cat ~/hello/world/templates/world/top.html
Hello world
~/hello/$ vim ~/hello/world/views.py
~/hello/$ vim ~/hello/hello/urls.py
from django.shortcuts import render # Create your views here. def top(request): # ← 追加する return render(request, 'world/top.html') # ← 追加する
"""hello URL Configuration (省略) """ from django.contrib import admin from django.urls import path from world.views import top # ← 追加する urlpatterns = [ path('admin/', admin.site.urls), path('', top, name='top'), # ← 追加する ]
5-1. ユニットテストの定義を作成する。
~/hello/$ vim ~/hello/world/tests.py5-2. ユニットテストを実行する。
各チェック用関数の名称は先頭に test_ を付加して記述する。
from django.test import TestCase # Create your tests here. class TopPageTest(TestCase): # ステータスコードのチェック def test_response(self): res = self.client.get("/") self.assertEqual(res.status_code, 200) # コンテンツのチェック def test_content(self): res = self.client.get("/") self.assertEqual(res.content, b'Hello world\n')
~/hello/$ python3 ./manage.py test
· 問題がない場合
· 問題がある場合ここでは見易いように太字にしています。実際は太字になりません。
Creating test database for alias 'default'... System check identified no issues (0 silenced). .. ---------------------------------------------------------------------- Ran 2 tests in 0.011s OK # ← OK が表示されること Destroying test database for alias 'default'...
ユニットテストの結果が FAILED になる場合は、ここまで作成したファイルのどこかが指定と異なっている。
ユニットテスト自体に誤りがあるかもしれない。
誤りを修正して結果を OK にすること。
下記では作成したテスト test_content(), test_response() でエラーが発行されている。
テストは定義順でなく、アルファベット順に実行される。
ここでは見易いように太字にしています。実際は太字になりません。
Creating test database for alias 'default'... System check identified no issues (0 silenced). FF ====================================================================== FAIL: test_content (world.tests.TopPageTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/who/hello/world/tests.py", line 13, in test_content self.assertEqual(res.content, b'Hello world\n') AssertionError: b'hello world\n' != b'Hello world\n' ====================================================================== FAIL: test_response (world.tests.TopPageTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/who/hello/world/tests.py", line 9, in test_response self.assertEqual(res.status_code, 200) AssertionError: 404 != 200 ---------------------------------------------------------------------- Ran 2 tests in 0.198s FAILED (failures=2) # ← FAILED が表示された Destroying test database for alias 'default'...
6-1. テンプレートファイルを作成する。
~/hello/$ vim ~/hello/world/templates/world/display.html (新規作成)6-2. ディスパッチャを設定する。
~/hello/$ vim ~/hello/world/templates/world/edit.html (新規作成)
<html> <head> <meta charset=utf-8> <title>World Display</title> </head> <body> <p>World Display</p> <p>ID は {{ ID }} です。</p> </body> </html>
<html> <head> <meta charset=utf-8> <title>World Edit</title> </head> <body> <p>World Edit</p> <p>ID は {{ ID }} です。</p> </body> </html>
~/hello/$ vim ~/hello/world/views.py6-3. 動作確認
~/hello/$ vim ~/hello/world/urls.py (新規作成)
from django.shortcuts import render # Create your views here. def top(request): return render(request, 'world/top.html') def world_display(request, world_id): # ← 追加する return render(request, 'world/display.html', {'ID':world_id}) # ← 追加する def world_edit(request, world_id): # ← 追加する return render(request, 'world/edit.html', {'ID':world_id}) # ← 追加する
~/hello/$ vim ~/hello/hello/urls.py
from django.urls import path from world import views urlpatterns = [ path('<int:world_id>/', views.world_display, name='world_displlay'), path('<int:world_id>/edit/', views.world_edit, name='world_edit' ), ]
"""hello URL Configuration (省略) """ from django.contrib import admin from django.urls import path from django.urls import include # ← 追加する from world.views import top urlpatterns = [ path('admin/', admin.site.urls), path('', top, name='top'), path('world/', include('world.urls')), # ← 追加する ]
6-3-1. テストサーバを起動する。
~/hello/$ python3 ./manage.py runserver 0:80006-3-2. ブラウザを起動し、テストサーバにアクセスする。
認証機能を組み込んでいないため、まだ「認証コンテンツ」になっていない。
>_ Windows PowerShell
Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. 新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/ ⏎ ← 通常コンテンツを表示する場合 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1 ⏎ ← 認証コンテンツを表示する場合 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1/edit ⏎ ← 認証コンテンツを編集する場合 PS C:\Users\who>
通常コンテンツを表示
認証コンテンツを表示
編集コンテンツを表示 (ここでは編集機能を実装しません)
View に認証機能を組み込む前に、認証機能の一部となるログインページを作成する。
7-1. Django アプリケーション accounts を追加する。
7-1-1. アプリケーションを追加する。
~/hello/$ python3 ./manage.py startapp accounts7-1-2. アプリケーションをプロジェクトに追加する。
~/hello/$ LANG=c tree ~/hello/
/home/who/hello/ |-- accounts (startapp accounts の実行時に生成された) | |-- __init__.py ( 〃 ) | |-- admin.py ( 〃 ) | |-- apps.py ( 〃 ) | |-- migrations ( 〃 ) | | `-- __init__.py ( 〃 ) | |-- models.py ( 〃 ) | |-- tests.py ( 〃 ) | `-- views.py ( 〃 ) |-- db.sqlite3 |-- hello (省略) |-- manage.py |-- templates `-- world (省略)~/hello/$ cat ~/hello/accounts/apps.py
~/hello/$ vim ~/hello/hello/settings.py
from django.apps import AppConfig class AccountsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'accounts'
(省略) INSTALLED_APPS = [ (省略) 'django_extensions', 'world.apps.WorldConfig', 'accounts.apps.AccountsConfig', # ← 追加 ] (省略)
8-1. 認証用 URL (ログイン要求時にリダイレクトされる URL) を確認する。
8-2. テンプレートファイルを作成する。
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "from django.contrib.auth.urls import urlpatterns
for url in urlpatterns: print(url)"~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "print(settings.LOGIN_URL)"
<URLPattern 'login/' [name='login']> <URLPattern 'logout/' [name='logout']> <URLPattern 'password_change/' [name='password_change']> <URLPattern 'password_change/done/' [name='password_change_done']> <URLPattern 'password_reset/' [name='password_reset']> <URLPattern 'password_reset/done/' [name='password_reset_done']> <URLPattern 'reset// /' [name='password_reset_confirm']> <URLPattern 'reset/done/' [name='password_reset_complete']>
/accounts/login/
~/hello/$ mkdir -p ~/hello/accounts/templates/registration/8-3. ログイン・ログアウト用の View を urls.py に用意する。
ディレクトリ名を registration/ としているのは前バージョンからの名残り。整合性が合っていれば良いので、深い意味はない。
~/hello/$ vim ~/hello/accounts/templates/registration/login.html (新規作成)
~/hello/$ vim ~/hello/accounts/templates/registration/logout.html (新規作成)
<html> <head> <meta charset=utf-8> <title>ログインページ</title> </head> <body> <p> <form method=post> {% csrf_token %} <input type=hidden name=next value="{{ next }}" /> ログイン<br> ユーザ名 <input type=text name=username maxlength=150><br> パスワード <input type=password name=password><br> <button type=submit>ログインする</button><br> </form> </p> </body> </html>
<html> <head> <meta charset=utf-8> <title>ログアウトページ</title> </head> <body> <p> ログアウトしました<br> <a href="/">トップページへ</a><br> </p> </body> </html>
ログイン・ログアウトページは contrib.auth.views の LoginView / LogoutView を使用するので views.py を使用しない。8-4. ディスパッチャを設定する。
~/hello/$ vim ~/hello/accounts/urls.py (新規作成)
route='···' は上記 8-1 で確認した URL。
from django.urls import path from django.contrib.auth.views import LoginView, LogoutView urlpatterns = [ path(route='login/', name='login', view=LoginView.as_view( redirect_authenticated_user=True, template_name='registration/login.html', ), ), path(route='logout/', name='logout', view=LogoutView.as_view( template_name='registration/logout.html', ), ), ]
~/hello/$ vim ~/hello/hello/urls.py
"""hello URL Configuration (省略) """ from django.contrib import admin from django.urls import path from django.urls import include from world.views import top urlpatterns = [ (省略) path('', top, name='top'), path('world/', include('world.urls')), path('accounts/', include('accounts.urls')), # ← 追加する ]
9-1. 認証用デコレータを確認する。
9-2. View に認証機能 (ログイン要求) を組み込む。
~/hello/$ python3 ./manage.py shell_plus -c "from django.contrib.auth import decorators
help(decorators)" | catデコレータ login_required / permission_required / user_passes_test の存在が確認できる。
今回は login_required を使用する。
# Shell Plus Model Imports (省略) NAME django.contrib.auth.decorators FUNCTIONS login_required(function=None, redirect_field_name='next', login_url=None) Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. permission_required(perm, login_url=None, raise_exception=False) Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page if necessary. If the raise_exception parameter is given the PermissionDenied exception is raised. user_passes_test(test_func, login_url=None, redirect_field_name='next') Decorator for views that checks that the user passes the given test, redirecting to the log-in page if necessary. The test should be a callable that takes the user object and returns True if the user passes. DATA REDIRECT_FIELD_NAME = 'next' settings = <LazySettings "hello.settings"> FILE /usr/local/lib/python3.6/site-packages/django/contrib/auth/decorators.py
~/hello/$ vim ~/hello/world/views.py9-3. テンプレートにログアウトの機能を追加する。
from django.shortcuts import render from django.contrib.auth.decorators import login_required # ← 追加する # Create your views here. # top は通常コンテンツなので認証の対象外。(@login_required を記述しない) def top(request): return render(request, 'world/top.html') @login_required # ← 追加する def world_display(request, world_id): return render(request, 'world/display.html', {'ID':world_id}) @login_required # ← 追加する def world_edit(request, world_id): return render(request, 'world/edit.html', {'ID':world_id})
この認証機能はクッキーによって許可情報を保持するため、 認証ページに無事ログインしたあとは、
クッキーが消滅するまで、ブラウザを終了してもログインしたままになります。(2 週間)
クッキーを無効にするため、ログアウトへのリンクを認証コンテンツのテンプレートに追加します。
~/hello/$ vim ~/hello/world/templates/world/display.html
~/hello/$ vim ~/hello/world/templates/world/edit.html
<html> <head> <meta charset=utf-8> <title>World Display</title> </head> <body> <p>World Display</p> <p>ID は {{ ID }} です。</p> <p><a href=/accounts/logout/>ログアウトする</a></p> # ← 追加する </body> </html>
クッキーの生存時間を確認するにはこちら。(日単位)
<html> <head> <meta charset=utf-8> <title>World Edit</title> </head> <body> <p>World Edit</p> <p>ID は {{ ID }} です。</p> <p><a href=/accounts/logout/>ログアウトする</a></p> # ← 追加する </body> </html>
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "print(settings.SESSION_COOKIE_AGE/(24*3600))"
14.0
10-1. 管理ユーザを作成する。
~/hello/$ python3 ./manage.py createsuperuser --username=admin --email=admin@example.jp10-2. 通常ユーザを作成する。
Password: adminpassword ⏎ Password (again): adminpassword ⏎ Superuser created successfully.
10-3. 作成したユーザを確認する。
~/hello/$ python3 ./manage.py shell_plus -c "
User.objects.create_user(username='user1', email='user1@example.jp', password='password')"
10-A. ユーザを削除する場合はこちら。
~/hello/$ python3 ./manage.py shell_plus --quiet-load -c "
for user in User.objects.all():
print(user.id, user.username, user.email)
print(user.password)"
↑ 行頭の空白文字も忘れずに。
1 admin admin@example.jp pbkdf2_sha256$260000$EdRL9JGDbcYtyw2vsCc1xC$gPZqn1JOmKzTAtA1MQHC8S+U6iOTZmPe1xVH/4sVzyU= 2 user1 user1@example.jp pbkdf2_sha256$260000$AxaXla31DZwBGZbeBMkOF7$KDSgqIiPh8yODDEr+wWdn/2Eze9LNKlwGRM0Q7WqIeQ=
~/hello/$ python3 ./manage.py shell_plus -c "User.objects.get(username='user1', is_superuser=False).delete()"
管理ユーザを削除するときは is_superuser=True を指定する。
11-1. テストサーバを起動する。
~/hello/$ python3 ./manage.py runserver 0:800011-2. ブラウザを起動し、テストサーバにアクセスする。
認証機能を組み込んだため、認証コンテンツをアクセスしようとするとログインページにリダイレクトされる。
>_ Windows PowerShell
Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. 新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/ ⏎ ← 通常コンテンツを表示する場合 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1 ⏎ ← 認証コンテンツを表示する場合 PS C:\Users\who> &"C:\Program Files\Mozilla Firefox\firefox.exe" 192.168.11.7:8000/world/1/edit ⏎ ← 認証コンテンツを編集する場合 PS C:\Users\who>
認証コンテンツを表示しようとすると、ログインページが表示される。
上記 10 で作成したユーザの ID, パスワードを記入し
ボタンをクリックする。
(ログインが失敗すると再度ログインページが表示される)
↓
ログインが成功すれば、目的のページが表示される。
「ログアウトする」をクリックすると、
ブラウザに保持されている認証用クッキーが無効になり、ログアウトページが表示される。
↓