
目次
こんにちは。sinyです。
この記事では、Djangoのユーザセッション管理機能の実装事例についてご紹介します。
通常のユーザ認証機能の実装
まずは、通常のDjangoユーザ認証の実装を行います。
settings.pyの設定
まずは、認証系に必要なsettngis.pyの設定を追加します。
LOGIN_URL='/accounts/' LOGIN_REDIRECT_URL='/accounts/' LOGOUT_REDIRECT_URL='/accounts/login'
| パラメータ | 説明 |
|
LOGIN_URL |
ログオンが必要なページに認証していないユーザがアクセスした場合にリダイレクトするURLを指定 |
|
LOGIN_REDIRECT_URL |
ログオン後にリダイレクトされるURLを指定 |
|
LOGOUT_REDIRECT_URL |
ログアウト後にリダイレクトされるURLを指定 |
ユーザ認証用アプリケーションの作成
以下のコマンドでユーザ認証用に新規アプリケーション(accounts)を作成します。
python manage.py startapp accounts
また、settings.pyのINSTALLED_APPに「 'accounts.apps.AccountsConfig',」を追加しておきます。
urls.pyの設定
まず、プロジェクト直下のurls.pyを以下の通り設定します。
from django.contrib import admin
from django.urls import path,include
from django.conf.urls import include, url
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
]
続いて、accountsアプリケーション直下のurls.pyを以下の通り設定します。
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
path('login/', views.Login.as_view(), name='login'),
path('logout/', views.Logout.as_view(), name='logout'),
path('', views.index, name='index'),
]
- 4行目:accountsアプリケーションのURLの名前空間名を指定しています。
- 7行目:ログオン認証画面用のURLパターンを定義しています。(http://127.0.0.1/login)
- 8行目:ログアウト用のURLパターンを定義しています。
- 9行目:ログオン後のINDEXページを定義します。
ログオン用フォームの作成
accountsアプリケーション配下にforms.pyを新規作成し、以下の通り設定します。
from django.contrib.auth.forms import(
AuthenticationForm
)
class LoginForm(AuthenticationForm):
#ログオンフォームの定義
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for fields in self.fields.values():
fields.widget.attrs['class'] = 'form-control'
fields.widget.attrs['placeholder']= fields.label
- 1~3行目:Django標準のAuthenticationFormクラスを利用すると簡単にログオン用フォームを生成することができるため、django.contrib.auth.formsからAuthenticationFormをインポートしています。
- 5行目:AuthenticationFormクラスを承継してログオン用のフォームクラス(LoginForm)を定義しています。
-
9~11行目:ここでは以下の設定を行っています。
①全てのフォームの部品のclass属性に「form-control」を指定(bootstrapのフォームデザインを利用するため)
②全てのフォームの部品にpaceholderを定義して、入力フォームにフォーム名が表示されるように指定。
※上記のすべてのフォームとは、AuthenticationFormクラス内で定義されているfields(フォームのカラム:ユーザ名、パスワード)のことです。
※Djangoではウィジェット(widget)と呼ばれる HTML の入力エレメントを表現するためのオブジェク トが存在していて、field.widget.attrs[属性] = <設定値>という形でフォームのデザインを定義することができます。
accountsアプリケーションのviews.py設定
accountsアプリケーションのviews.pyに以下のコードを追記します。
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import(
LoginView, LogoutView
)
from . forms import LoginForm
class Login(LoginView):
#ログインページ
form_class = LoginForm
template_name = 'accounts/login.html'
class Logout(LoginRequiredMixin, LogoutView):
#ログアウトページ
template_name = 'accounts/login.html'
def index(request):
return render(request, 'accounts/index.html')
- 9行目:LoginViewクラスを承継して、ログオン用クラス(Login)を定義しています。
- 11行目:form_classでログオンページに利用するフォームクラス(LoginForm)を指定しています。
- 12行目:ログオンページ用のテンプレートファイルを指定しています。
- 15行目:LoginRequireMixin、LogoutViewクラスを承継してログアウト用のクラス(Logout)を定義しています。
LoginRequireMixinクラスを承継しているのは、ログオンしているユーザでないとログアウトボタンを押せないようにするためです。(LoginRequiremixinはログオンしているユーザだけが制御できるようにするためのクラス)
template_nameでログアウト後に表示されるテンプレートファイルを指定します。 - 20行目:ログオン後に遷移するテスト用のTOPページを定義した関数です。
今回は、特にモデルの定義はしないので、一旦このままマイグレーションを実行しておきます。
python manage.py makemigrations python manage.py migrate
テンプレートの作成
まずは、テンプレートの場所をプロジェクト直下のtemplatesに変更するためにsettings.pyに以下の設定をします。
'DIRS': [os.path.join(BASE_DIR, 'templates')],
EMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # DIRSに設定を入れる。
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
プロジェクトフォルダ直下に「\templates\accounts」を作成します。
続いて、base.html(共通テンプレート)、index.html(ログオン後のTOP画面),login.html(ログオン画面)を以下の通り作成します。
※テンプレート内の細かいデザインの設定部分は設定例ですので、必ずしも同じでなくてよいです。
■base.html
<!doctype html>
<html lang="ja">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
<title>Tutorialサイト</title>
</head>
<body>
<!-- ナビバー -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'accounts:login' %}">Tutorialサイト</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
{% if user.is_authenticated %}
<a class="btn btn-primary" href="{% url 'accounts:logout' %}">ログアウト</a>
{% endif %}
</div>
</nav>
<!-- メインコンテント -->
<div class="container mt-3">
{% block content %}{% endblock %}
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
crossorigin="anonymous"></script>
</body>
</html>
認証済みユーザの場合、Userオブジェクトのis_authenticatedにTrueが設定されます。
■index.html
{% extends "accounts/base.html" %}
{% block content %}
ログオンしました。
{% endblock %}
■login.html
{% extends "accounts/base.html" %}
{% block content %}
<form action="" method="POST">
<div class="col-md-6 offset-md-3">
<div class="card">
<div class="card-body">
{% for field in form %}
{{ field }}
<hr>
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger" role="alert">
<p>{{ error }}</p>
</div>
{% endfor %}
<button type="submit" class="btn btn-success btn-lg btn-block" >ログイン</button>
<input type="hidden" name="next" value="{{ next }}" />
{% csrf_token %}
</div>
</div>
</div>
</form>
{% endblock %}
- 9~12行目:テンプレートタグのfor文でformオブジェクトから要素を1つずつfield変数に格納し、{{field}}でフォームを表示しています。
-
14~18行目: 認証時のエラーは form.non_field_errorsから取得できます。
エラー内容は{{ error }}と記載することで表示できます。 -
以下のようにfor文の中にdivのclassでalert-dangerを指定することでエラーが発生した場合に赤枠でエラー内容が表示されるように設定しています。

以上で、http://127.0.0.1:8000/accounts/login/にアクセス後にログオンすると、以下のようにログオン画面が表示されます。

ログオンすると、以下のようなTOP画面にログオンします。

さて、ここまでは通常のユーザ認証機能の実装でした。
いよいよ、本題のユーザセッション管理機能を実装していきます。
ユーザセッション管理機能の実装
まず、必要なモジュールをpipでインストールします。
pip install django-user-sessions
settings.pyのINSTALLED_APPS に 'user_sessions' を追加します。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user_sessions', #追加
'accounts.apps.AccountsConfig',
]
settings.pyのMIDDLEWAREに「'user_sessions.middleware.SessionMiddleware',」を追加します。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'user_sessions.middleware.SessionMiddleware', #追加
]
また、settings.pyの末尾に「SESSION_ENGINE = 'user_sessions.backends.db'」を追加します。
次に、プロジェクト直下のurls.pyに以下の設定を追加します。
from django.contrib import admin
from django.urls import path,include
from django.conf.urls import include, url
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('', include('user_sessions.urls', 'user_sessions')), #追加
]
この時点で、再度「python manage.py migrate」を実行します。
以上で設定は完了です。
http://127.0.0.1:8000/account/sessions/にアクセスすると、以下のようなセッション管理画面が表示されます。

デフォルトだと上記画面の通り、ユーザ名が表示されていないのと、英語表記なのでカスタマイズしてみます。
上記画面のテンプレートをカスタマイズするには「Lib\site-packages\user_sessions\templates\user_sessions\session_list.html」を以下の通り修正します。
{% extends "user_sessions/_base.html" %}
{% load user_sessions i18n %}
{% block content %}
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
{% trans "<em>unknown</em>" as unknown %}
<h1>{% trans "アクティブセッション一覧" %}</h1>
<table class="table">
<thead>
<tr>
<th>{% trans "IPアドレス" %}</th>
<th>{% trans "ユーザ名" %}</th>
<th>{% trans "デバイス" %}</th>
<th>{% trans "最終アクション" %}</th>
<th>{% trans "セッションの終了" %}</th>
</tr>
</thead>
{% for object in object_list %}
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
<td>{{ object.ip}}</td>
<td>{{ object.user }} </td>
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
<td>
{% if object.session_key == session_key %}
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
{% else %}
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
{% endif %}
</td>
<td>
<form method="post" action="{% url 'user_sessions:session_delete' object.pk %}">
{% csrf_token %}
{% if object.session_key == session_key %}
<button type="submit" class="btn btn-xs btn-link">{% trans "End Session" %}</button>
{% else %}
<button type="submit" class="btn btn-xs btn-warning">{% trans "End Session" %}</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</table>
{% if object_list.count > 1 %}
<form method="post" action="{% url 'user_sessions:session_delete_other' %}">
{% csrf_token %}
<p>{% blocktrans %}You can also end all other sessions but the current.
This will log you out on all other devices.{% endblocktrans %}</p>
<button type="submit" class="btn btn-default btn-warning">{% trans "End All Other Sessions" %}</button>
</form>
{% endif %}
{% endblock %}
{{ object.user }} → ユーザ名が表示されるように追加
再度、「http://127.0.0.1:8000/account/sessions/」にアクセスすると以下のような画面に変わります。

画面右側の「End Session」をクリックすると自分のセッションを切断することができます。
※上記管理画には自分のセッションしか表示されないようです。
上記画面は、自分自身のセッションしか切断できませんが、Adminサイト上からは全ユーザのセッションを切断することができます。
adminサイトにログオンすると「セッション」が追加されているのでクリックします。

ログオンしている全ユーザのセッション情報が表示されるので、切断したいユーザにチェックを入れてセッション切断を実行すれば、セッションをKILLすることができます。

以上、「Djangoユーザセッション管理画面の実装」でした。
参考になれば幸いです。

