Python + Djangoで独自のユーザ認証を実装する

Djangoの認証機能を利用して簡単に認証機能を実装してみます

環境:
OS: Windows10 Home 64bit
Python 3.6.5
Django 2.0.6

[Python + Djangoのプロジェクト作成](https://qiita.com/tiguchi919/items/f658d607888449cfdf23)でプロジェクトを作成します

####1.migrate

python manage.py migrateを実行して、認証系のテーブルを作成します。

```cmd.prompt
(venv) C:\data\python\work\myproject>python manage.py migrate
```

###2.管理ユーザを作成する

管理ユーザを作成します。このユーザで認証を行うことになります。

```cmd.prompt
python manage.py createsuperuser
Username (leave blank to use 'papa'): django
Email address: django@localhost
Password:
Password (again):
Superuser created successfully.
```

###3.アプリケーションの構成

完成後のアプリケーションの構成は以下のとおりです
JQuery, Bootstrap4を使いますので、ダウンロードして配置しておきます

```
C:\data\python\work\myproject
├─accounts・・・・(1)
│  │  admin.py
│  │  apps.py
│  │  models.py
│  │  tests.py
│  │  urls.py・・・・(2)
│  │  views.py・・・・(3)
│  │  __init__.py
│  │  
│  ├─migrations
・・・
│  │          
│  └─__pycache__
・・・
├─myproject
│  │  settings.py・・・(8)
│  │  urls.py・・・・・(9)
│  │  wsgi.py
│  │  __init__.py
│  │  
│  └─__pycache__
・・・
├─static
│  ├─css
│  │      bootstrap.min.css
│  │      bootstrap.min.css.map
│  │      style.css・・・・(4)
│  └─js
│         bootstrap.bundle.min.js
│         jquery-3.3.1.min.js
└─templates
    ├─accounts
    │      top.html・・・・(5)
    ├─commons
    │      base.html・・・・(6)
    └─registration
           login.html・・・・(7)
```

(1)accounts

Djangoの認証機能を作成するには、accountsアプリケーションを作成します

```cmd.prompt
(venv) C:\data\python\work\myproject>python manage.py startapp accounts
```

(2)accounts\urls.py

accounts\urls.pyを作成します。

```accounts\urls.py
from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    path('', views.index, name='index'),
]
```

(3)accounts\views.py

@login_required をつけると、このviewにアクセスするときに認証が必要になります

```accounts\views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def index(request):
    return render(request, 'accounts/top.html')
```

(4)static\css\style.css

画面全体のスタイルを定義します。

```static\css\style.css
@charset "UTF-8";

.mysystem-header{
  background: #3333FF !important;
  color: white !important;
}
.mysystem-border{
  background: #3333FF !important;
  color: white !important;
}
```

(5)accounts\top.html

認証後に表示するtop画面を作成します

```accounts\top.html

{% extends "commons/base.html" %}

{% block title %}メニュー{% endblock %}

{% block styles %}
nav.menu ul li {
    list-style-type:none;
    font-size: 1.1em;
}
{% endblock %}

{% block headertitle %}メニュー画面{% endblock %}

{% block content %}
<nav class="menu">
  <ul>
      <li>
        <a target="_blank" href="/hello">Hello World!</a>
      </li>
  </ul>
</nav>
{% endblock %}

{% block scripts %}
<script>
</script>
{% endblock %}
```

(6)commons\base.html

共通テンプレートです

```commons\base.html
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
    <title>
      {% block title %}{% endblock %}
    </title>
    <style>
        body {
          padding : 10px;
        }
        div.container {
          min-width:600px;
        }
        nav ul#menu {
            margin:0px;
            padding:0px;
        }
        nav ul#menu li {
            float:right;
            list-style-type:none;
            text-decoration: underline;
            padding-left: 10px;
        }
        div.header {
          padding: 10px;
        }
        div.header * {
          color: white !important;
        }
        div.header .title {
          font-size: 1.2em;
        }
        {% block styles %}{% endblock %}
    </style>
</head>
<body>
<div class="container">
  <div class="inner">
    <div class="row header mysystem-header">
      <div class="col-sm title">
        {% block headertitle %}{% endblock %}
      </div>
      <div class="col-sm">
        <nav class="pull-right">
            <ul id="menu">
              {% if user.is_authenticated %}
                <li><a href="{% url 'logout' %}" class="logout">Logout</a></li>
              {% else %}
              {% endif %}
            </ul>
        </nav>
      </div>
    </div>
    <br/>
    <div class="content">
        {% block content %}{% endblock %}
    </div>
  </div>
</div>
<script src="{% static "js/jquery-3.3.1.min.js" %}"></script>
<script src="{% static "js/bootstrap.bundle.min.js" %}"></script>
{% block scripts %}{% endblock %}
</body>
</html>
```

(7)registration\login.html

ログイン画面です
:point_up_2:templateファイルは、「registration」フォルダに入れておく必要があります

```registration\login.html
{% load staticfiles %}
{% load bootstrap4 %}
<!DOCTYPE html>
<html lang="ja-JP" >
<head>
    <title>ログイン</title>
    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" type="text/css" href="/static/admin/css/responsive.css" />
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <style>
    .login #container {
        width: 30em !important;
        min-width: 300px;
        margin: 100px auto;
        border-style: solid;
        border-color: #3333FF;
    }
    div.form-group {
      padding-top: 20px;
    }
    div#header {
      padding: 10px;
    }
    div#content-main {
      padding: 20px;
    }
    </style>
</head>

<body class=" login" data-admin-utc-offset="32400">
<div id="container">
  <div class="inner">
    <div id="header" class="mysystem-header">
              ○○管理システム
    </div>
    <div id="content" class="colM">
      <div id="content-main">
        <form method="post" action="{% url 'login' %}">
        {% csrf_token %}
        {% bootstrap_form form layout='horizontal' %}
        <div class="form-group row px-3">
          <input type="submit" class="btn btn-primary btn-block" value="ログイン" />
        </div>
        <input type="hidden" name="next" value="{{ next }}" />
        </form>
      </div>  <!-- content-main -->
      <br class="clear" />
    </div>  <!-- content -->
  </div>  <!-- inner -->
</div>  <!-- END Container -->
</body>
</html>
```
:point_up_2:bootstrap_form でformを描画しています。
{% bootstrap_form form layout='horizontal' %}


(8)settings.py

settings.pyを編集します

```settings.py
INSTALLED_APPS = [
    'accounts.apps.AccountsConfig', #追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap4', #追加
]
・・・・
LOGOUT_REDIRECT_URL='/'  #任意の行に追加
```

bootstrap4は、pipでインストールできます
ログアウトでは、ルートを表示するようにしておきます。

(9)myproject\urls.py

・accountsにアクセスすると、Djagoの認証用のサイトにアクセスします
・ルートにアクセスすると、「accounts」アプリのサイトにアクセスします

```myproject\urls.py
・・・
urlpatterns = [
    path('accounts/', include('django.contrib.auth.urls')), #  追加
    path('', include('accounts.urls')), #"追加"
    path('admin/', admin.site.urls),
]
```

ルートのURLをaccountsにすることで、topページが表示されるようにしておきます。

動作確認してみます。

```cmd.prompt
(venv) C:\data\python\work\myproject>manage.py runserver
```

http://localhost:8000/にアクセスします

![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/eae16726-8efc-97ff-28af-b7ac59647412.png)

「2.管理ユーザを作成する」で作成したスーパーユーザでログインしてみます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/1876b93f-f992-d6a5-ab3e-d6ad972812d0.png)

ログインできました。Djangoは、認証の実装も簡単ですね。