Djangoを使用して認証機能を組み込む際は、基本的にデータベースにアカウント情報を保存し、認証機能を実現していると思います。
本記事では、データベースを使用せずに、csvでユーザー名・パスワードを一元管理し、認証機能を実装する方法をご紹介いたします。
Djangoに関する基本的な知識やアーキテクチャに関しては割愛しておりますこと、ご了承願います。
完成イメージ
このように、csvでアカウントを管理していきます。
今回は「accounts.csv」という名前でローカルに保存します。
本記事では開発環境での解説ですが、本番運用時には、S3やGCSなどに保存して、読み取る形になるのかなと思います。
実際のアプリケーション上はこんな感じです。
トップページには「ログインページ」「ダッシュボード」へのアクセスリンクがあります。
ログインしていない状態でダッシュボードにアクセスしようとすると、強制的にログインページにリダイレクトされます。
記事が冗長化してしまうため、各ページの内容は簡略化し、cssも一切設定しておりません。
準備
プロジェクト、アプリの作成
まずは、Djangoプロジェクトの作成を行います。
下記の記事を参考に、
- 3. 仮想環境の構築
- 4. Djangoプロジェクトの作成
- 5. Djangoアプリケーションを作成する
までを行います。
プロジェクト名、アプリ名はなんでも構いません。
今回は下記のようなプロジェクト、アプリケーションを作成したという前提で進めていきます。
- プロジェクト名:test_project
- アプリケーション名:csv_auth_app
ルーティング
まずはプロジェクトルーティングを設定します。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('csv_auth_app.urls')),
]
続いてアプリケーション側でのルーティングです。
トップページ、ダッシュボード、ログインページへのルーティングを作成します。
from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('dashboad/', views.DashboadView.as_view(), name='dashboad'),
path('login/', views.CSVLoginView.as_view(), name='login'),
]
テンプレートの作成
下記の3種類のテンプレートを用意します。
- トップページ
- ダッシュボード
- ログイン画面
誰でもトップページにアクセスできますが、ログインしてない状態でダッシュボードに移動しようとすると、強制的にログイン画面にリダイレクトするよう実装していきます。
本題とは関係ないので、サクサクいきます。
「csv_auth_app/templates/」配下に、下記の4つのテンプレートを作成します。
ベース
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock title %}</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
{% block head %}{% endblock head %}
</head>
<body>
{% block contents %}{% endblock contents %}
</body>
</html>
トップページ
{% extends 'base.html' %}
{% load static %}
{% block title %}TOP{% endblock title %}
{% block contents %}
<div>
<a href="{% url 'login' %}">login</a>
<a href="{% url 'dashboad' %}">dashboad</a>
</div>
{% endblock contents %}
ダッシュボード
{% extends 'base.html' %}
{% load static %}
{% block title %}DASHBOAD{% endblock title %}
{% block contents %}
<div>
<h1>Dashboad</h1>
</div>
{% endblock contents %}
ログイン画面
{% extends 'base.html' %}
{% load static %}
{% block title %}LOGIN{% endblock title %}
{% block contents %}
<div>
<h1>Login</h1>
</div>
{% endblock contents %}
準備は以上です。
csv認証を実装
ここから実際に、csvのアカウントデータによる認証機能を実装していきます。
forms.py
まずはフォームの設定です。
from django import forms
class CSVAuthForm(forms.Form):
username = forms.CharField(max_length=254)
password = forms.CharField(widget=forms.PasswordInput)
フォーム項目、すなわちログインに必要な項目はユーザー名とパスワードです。
さらに、ログインテンプレートにフォームを追加します。
{% extends 'base.html' %}
{% load static %}
{% block title %}LOGIN{% endblock title %}
{% block contents %}
<div>
<h1>Login</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
{% if form.errors %}
<p class="error">ユーザー名またはパスワードが正しくありません。</p>
{% endif %}
</div>
{% endblock contents %}
models.py
続いて、カスタムモデルの設定です。
import csv
class Account:
def __init__(self, username, password, level):
self.username = username
self.password = password
self.level = level
class CSVAccountManager:
def __init__(self, csv_file):
self.csv_file = csv_file
def get_account(self, username):
"""
csvを読み込み、その中に "username" に該当するレコードがあれば Account オブジェクトを返す。
なければ None を返す。
"""
with open(self.csv_file, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
if row['username'] == username:
return Account(
username=row['username'],
password=row['password'],
level=row['level']
)
return None
def validate_account(self, username, password):
"""
1. 入力したユーザー名に対応したアカウント情報がcsvに存在すれば、Accountオブジェクトが返る。
2. 入力したパスワードと、csvのパスワードが一致すれば、Accountオブジェクトをそのまま返す。なければ None を返す。
"""
account = self.get_account(username)
if account and account.password == password:
return account
return None
views.py
さらにビューの設定です。
from django.views import generic, View
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect
from django.conf import settings
from .forms import CSVAuthForm
from .models import CSVAccountManager
class IndexView(generic.TemplateView):
""" トップページのビュー """
template_name = 'index.html'
class CSVLoginView(View):
""" ログインページのビュー """
template_name = 'login.html'
form_class = CSVAuthForm
def get(self, request):
# ログイン情報初期化 → ログインページの表示
self.request.session['is_authenticated'] = False
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
# ローカルのプロジェクト直下に「accounts.csv」を配置している前提
account_manager = CSVAccountManager(f'{settings.BASE_DIR}/accounts.csv')
account = account_manager.validate_account(username, password)
# accountがあれば、ログイン情報付与 → トップページにリダイレクト
if account:
self.request.session['is_authenticated'] = True
return HttpResponseRedirect('/')
else:
form.add_error(None, 'Invalid username or password.')
return render(request, self.template_name, {'form': form})
class DashboadView(generic.TemplateView):
""" ダッシュボードのビュー """
template_name = 'dashboad.html'
def get(self, request):
# 認証情報が通ってれば、ダッシュボード表示
# 通っていない場合は、ログインページにリダイレクト
if 'is_authenticated' in self.request.session:
is_authenticated = self.request.session['is_authenticated']
if is_authenticated == False:
return HttpResponseRedirect('/login/')
else:
return render(request, self.template_name)
else:
return redirect('login')
今回はセッションデータを使用して、ログインできているか否かを判断しています。
また、ログインしていないとダッシュボードにはアクセスできない設定なので、ダッシュボードにアクセスした際にはセッションデータをチェックし、認証情報が通っていなければログインページにリダイレクトさせています。
ログイン時には、accounts.csv内に、入力した「ユーザー名」「パスワード」の組み合わせのレコードがあれば、認証情報をセッションデータに付与します。
settings.py
最後にsettings.pyを編集します。
セッションデータ
セッションデータを有効にするために、
- INSTALED_APPS
- MIDDLEWARE
の中に下記のような設定があることを確認します。
INSTALLED_APPS = [
# ... 略 ...
'django.contrib.sessions',
# ... 略 ...
]
MIDDLEWARE = [
# ... 略 ...
'django.contrib.sessions.middleware.SessionMiddleware',
# ... 略 ...
]
さらに、セッションデータの保存場所を設定します。今回はキャッシュに保存します。
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
セッションをどこに保存できるかなどは下記の外部サイトに詳しく紹介されておりますので、引用させていただきます。
データベースの設定
今回はデータベースを使用しないので、下記のようにDATABASESの値を設定します。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.dummy',
}
}
以上です!
まとめ
csvデータを使用して、認証機能を実装する方法をご紹介させていただきました。
クラウドを使用したデータベースの維持費が高いので、デフォルトの方法以外で認証機能を実装できないかと思案したのが事の発端で、備忘録も兼ねています。何か改善点や他の良い方法などございましたら、ぜひご教示いただけると幸いでございます、、、。
ご覧いただきありがとうございました。
コメント