DjangoとAjax:Webアプリケーションのための実時間形式検証によるロバスト認証・認証システム‐4


最後に、学生登録制度を構築しました.私たちはいくつかの追加ファイルを作成する時点で停止しました.tasks.py and tokens.py . この部分では、実装を続けます.

ソースコード


このポイントへのソースコードはgithub アプリケーション全体のソースコードは以下の通りです.

シリンジ / Djangorest Realchen TimeRankの検証


DjangoとAJAX:Webアプリケーションのための実時間形式検証を用いたロバスト認証・認証システム


Djangorest Realchen TimeRankの検証


DjangoとAJAX:Webアプリケーションのための実時間形式検証を用いたロバスト認証・認証システム
View on GitHub

ステップ7 :トークン。PyとタスクPyファイル


このシリーズのパート3を締結しながら、我々はtokens.py and tasks.py ファイル.前者はユーザを検証するためにユニークなトークンを作成していますが、後者はcelery . このプロジェクトではcelery , 分散タスクキューは、送信メールを含むすべてのバックグラウンドタスクを処理します.これで、要件のこのセグメントを満たします.

...Time attacks must be addressed by sending the mails asynchronously...


の内容tokens.py とても簡単です.
# accounts > tokens.py

from django.contrib.auth.tokens import PasswordResetTokenGenerator

from six import text_type


class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
            text_type(user.pk)
            + text_type(timestamp)
            + text_type(user.is_student)
            + text_type(user.is_lecturer)
        )


account_activation_token = AccountActivationTokenGenerator()
基本的に、我々は、Djangoのものを継承していますPasswordResetTokenGenerator そして、ユーザのID(私たちの場合のUUID)、時間と他の特定のユーザー属性に基づいてハッシング.それはかなり安全でユニークです!そして、account_activation_token 我々が後に私たちのstudent_signup 関数.
実装するtasks.py , インストールする必要があります celery with Redis バックエンド.あなたのための完全な作業のセットアップを確認しますredis .
このプロジェクトの仮想環境ではpip or pipenv (初めにPipenvを使っていたなら)
┌──(sirneij@sirneij)-[~/Documents/Projects/Django/django_real_time_validation]
└─$[sirneij@sirneij django_real_time_validation]$ pipenv install "celery[redis]"
次に、celery.py プロジェクトのディレクトリにファイルします.プロジェクトのディレクトリとしてsettings.py ファイル.
┌──(sirneij@sirneij)-[~/Documents/Projects/Django/django_real_time_validation]
└─$[sirneij@sirneij django_real_time_validation]$ touch authentication/celery.py
に設定します.
# authentication > celery.py
import os

from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentication.settings")

app = Celery("authentication")

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object("django.conf:settings", namespace="CELERY")

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print(f"Request: {self.request!r}")

これはusing celery with django 行6と8の私のアプリの名前に置くのわずかな変更で.
アプリがDjangoは、@ ShareLearnタスクデコレータは、それを使用するように起動するときにロードされるように、このアプリをインポートすることを確認するにはproject_name/__init__.py :
# authentication > __init__.py
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ("celery_app",)
現在tasks.py :
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core import mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags

from celery import shared_task


@shared_task
def send_email_message(subject, template_name, user_id, ctx):
    html_message = render_to_string(template_name, ctx)
    plain_message = strip_tags(html_message)
    mail.send_mail(
        subject=subject,
        message=plain_message,
        from_email=settings.DEFAULT_FROM_EMAIL,
        recipient_list=[get_user_model().objects.get(id=user_id).email],
        fail_silently=False,
        html_message=html_message,
    )
セロリで飾られたシンプルな機能ですshared_task . これはDjangoのmail メッセージを送信するにはユーザーオブジェクトをセロリタスクに渡すことを保証することは非常に重要です.ユーザーモデルの1つの属性だけを渡しますuser_id , 解決策です.モデルオブジェクトまたはインスタンスを渡すと、一般的になりますObject not serializable エラーです.設定をラップするにはsettings.py このスニペット
CELERY_BROKER_URL = config("REDIS_URL", default="")
CELERY_RESULT_BACKEND = config("REDIS_URL", default="")
CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
あなたREDIS_URL ローカルREDISホストとポートredis://host:port ). 良い練習は、これをAに置くことです.env ファイルをアップロードするGitHub ファイルパスを含める.gitignore ファイルを他の人を参照してくださいにアップロードしないように.

ステップ8:学生登録機能をURLに再接続して接続します。パイ


準備が整ったので、調べてみようstudent_signup 表示機能は、最後の部分で書かれた.最初に、我々はStudentRegistrationForm そして、入ってくるリクエストがPOST . TRUEの場合、リクエストデータのコピーを作成し、email , username and password 入力されたリクエストユーザー.If the email 最後の部分で作成された規則に従って、ユーザーのインスタンスを作成し、ユーザーのテストを行いますpassword and email 他の検証に対して.スケールを通して、私たちは他のユーザのparamInterを作成したインスタンスに挿入し、ユーザーに確認のためのメールを送信します.celeryタスクに渡されたコンテキストに注意してください.
...
ctx = {
    "fullname": user.get_full_name(),
    "domain": str(get_current_site(request)),
    "uid": urlsafe_base64_encode(force_bytes(user.pk)),
    "token": account_activation_token.make_token(user),
            }
保証するget_current_site(request) , そうでなければcelery 連載できない問題request データ.
ユーザのパスワードとユーザ名が規則に従わない場合、データベースから削除されます.get_user_model().objects.get(email=post_data.get("email")).delete() . これを我々に加えましょうurls.py ファイル
# accounts > urls.py
...
urlpatterns = [
   ...
    path("student-sign-up/", views.student_signup, name="student_signup"),
]
また、ユーザが自分のメールをチェックする必要があることや、リンクをクリックした後にユーザーをアクティブにするために別のユーザーに通知する機能も必要です.
# accounts > views.py
...
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
...

def activate(request, uidb64, token):
    try:
        uid = force_text(urlsafe_base64_decode(uidb64))
        user = get_user_model().objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError):
        user = None
    # checking if the user exists, if the token is valid.
    if user is not None and account_activation_token.check_token(user, token):
        # if valid set active true
        user.is_active = True
        user.save()
        messages.success(
            request, f"Your email has been verified successfully! You are now able to log in."
        )
        return redirect("accounts:login")
    else:
        return render(request, "accounts/activation_invalid.html")


def activation_sent_view(request):
    return render(request, "accounts/activation_sent.html")
The activate 関数はuidb64 ユーザを取得するには、トークンを所属し、ユーザをアクティブにする前にトークンの妥当性をチェックします.
# accounts > views.py
...
user.is_active = True
user.save()
...
我々に彼らを含めましょうurls.py ファイル
# accounts > urls.py
...
urlpatterns = [
   ...
    path("sent/", views.activation_sent_view, name="activation_sent"),
    path("activate/<uidb64>/<token>/", views.activate, name="activate"),
]

ステップ9 :ログインやその他のテンプレートの作成


今まで何をしているかを見るために、HTMLとCSSを入れましょう.クリエイトaccounts/activation_sent.html (メール送信通知テンプレート)accounts/activation_invalid.html (無効なトークンテンプレート)accounts/student_signup.html (学生登録)accounts/activation_request.txt (テキストベースのメール用)accounts/activation_request.html ( HTMLベースのメール).
┌──(sirneij@sirneij)-[~/Documents/Projects/Django/django_real_time_validation]
└─$[sirneij@sirneij django_real_time_validation]$  touch templates/accounts/activation_sent.html templates/accounts/activation_invalid.html templates/accounts/student_signup.html templates/accounts/activation_request.txt templates/accounts/activation_request.html
activation_request.txt 次のようになります.
<!--templates/accounts/activation_request.txt-->

{% autoescape off %}
Hi {{ fullname }},
    Thank you for joining us on this great platform.
    Please click the following button to confirm your registration...


    By the way, if the above button is not clickable, paste the following link in your browser.
    http://{{ domain }}{% url 'accounts:activate' uidb64=uid token=token %}


Django Authentication Webmaster
{% endautoescape %}
メイクactivation_request.html 次のように表示されます.
<!--templates/accounts/activation_request.html-->

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width" />
    <style>
      * {
        margin: 0;
        padding: 0;
        font-size: 100%;
        font-family: "Avenir Next", "Helvetica Neue", "Helvetica", Helvetica,
          Arial, sans-serif;
        line-height: 1.65;
      }

      img {
        max-width: 100%;
        margin: 0 auto;
        display: block;
      }

      body,
      .body-wrap {
        width: 100% !important;
        height: 100%;
        background: #f8f8f8;
      }

      a {
        color: #206bc4;
        text-decoration: none;
      }

      a:hover {
        text-decoration: underline;
      }

      .text-center {
        text-align: center;
      }

      .text-right {
        text-align: right;
      }

      .text-left {
        text-align: left;
      }

      .button {
        display: inline-block;
        color: #ffffff;
        background: #206bc4;
        border: solid #206bc4;
        border-width: 10px 20px 8px;
        font-weight: bold;
        border-radius: 4px;
      }

      .button:hover {
        text-decoration: none;
        color: #ffffff;
        background-color: #1b59a3;
        border-color: #195398;
      }

      h1,
      h2,
      h3,
      h4,
      h5,
      h6 {
        margin-bottom: 20px;
        line-height: 1.25;
      }

      h1 {
        font-size: 32px;
      }

      h2 {
        font-size: 28px;
      }

      h3 {
        font-size: 24px;
      }

      h4 {
        font-size: 20px;
      }

      h5 {
        font-size: 16px;
      }

      p,
      ul,
      ol {
        font-size: 16px;
        font-weight: normal;
        margin-bottom: 20px;
      }

      .container {
        display: block !important;
        clear: both !important;
        margin: 0 auto !important;
        max-width: 580px !important;
      }

      .container table {
        width: 100% !important;
        border-collapse: collapse;
      }

      .container .masthead {
        margin-top: 20px;
        padding: 80px 0;
        background: #206bc4;
        color: #ffffff;
      }

      .container .masthead h1 {
        margin: 0 auto !important;
        max-width: 90%;
        text-transform: uppercase;
      }

      .container .content {
        background: #ffffff;
        padding: 30px 35px;
      }

      .container .content.footer {
        background: none;
      }

      .container .content.footer p {
        margin-bottom: 0;
        color: #888;
        text-align: center;
        font-size: 14px;
      }

      .container .content.footer a {
        color: #888;
        text-decoration: none;
        font-weight: bold;
      }

      .container .content.footer a:hover {
        text-decoration: underline;
      }
    </style>
    <title>Verify your email address.</title>
  </head>

  <body>
    <!-- auto -->
    {% autoescape off %}
    <table class="body-wrap">
      <tr>
        <td class="container">
          <!-- Message start -->
          <table>
            <tr>
              <td align="center" class="masthead">
                <h1>Welcome to Django Authentication System...</h1>
              </td>
            </tr>
            <tr>
              <td class="content">
                <h2>
                  Hi
                  <strong style="text-transform: capitalize"
                    >{{ fullname }}</strong
                  >,
                </h2>

                <p>Thank you for joining us on this great platform.</p>

                <p>
                  Please click the following button to confirm your
                  registration...
                </p>

                <table>
                  <tr>
                    <td align="center">
                      <p>
                        <a
                          href="http://{{ domain }}{% url 'accounts:activate' uidb64=uid token=token %}"
                          class="button"
                          >Yes, I'm in!</a
                        >
                      </p>
                    </td>
                  </tr>
                </table>

                <p>
                  By the way, if the above button is not clickable, paste the
                  following link in your browser.
                  <!-- email link -->
    http://{{ domain }}{% url 'accounts:activate' uidb64=uid token=token %}
                </p>

                <p><em>– Django Authentication Webmaster</em></p>
              </td>
            </tr>
          </table>
        </td>
      </tr>
      <tr>
        <td class="container">
          <!-- Message start -->
          <table>
            <tr>
              <td class="content footer" align="center">
                <p>
                  Sent by <a href="{{ domain }}">Django Authentication</a>,
                  Federal University of Technology, Akure, South Gate, Ondo
                  State, Nigeria.
                </p>
                <p>
                  <a href="mailto:[email protected]"
                    >[email protected]</a
                  >
                </p>
              </td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
    <!-- end auto -->
    {% endautoescape %}
  </body>
</html>

単純なHTMLファイル.これは、HTMLメールのいくつかのベストプラクティスが組み込まれています.activation_sent.html これは

<!--templates/accounts/activation_sent.html-->

{% extends 'base.html' %}
<!-- title -->
{% block title %} Verification email sent {% endblock title %}
<!-- static files -->
{% load static %}
<!-- content starts -->
{% block content %}

<div class="row center-content">
  <div class="col s12" style="max-width: 30rem">
    <div class="card blue-grey darken-1">
      <div class="card-content white-text">
        <span class="card-title">Thank you for creating an account!</span>
        <p>
          An email has been sent to the e-mail address you provided during
          registeration for confirmation.
        </p>
        <p>
          Make sure you visit the link provided in mail as it will soon be
          revoked.
        </p>
      </div>
    </div>
  </div>
</div>

<!-- content ends -->
{% endblock content %}

例えばactivation_invalid.html , 次のようになります.
{% extends 'base.html' %}
<!-- title -->
{% block title %} Verification email failed {% endblock title %}
<!-- static files -->
{% load static %}
<!-- content starts -->
{% block content %}

<div class="row center-content">
  <div class="col s12" style="max-width: 30rem">
    <div class="card blue-grey darken-1">
      <div class="card-content white-text">
        <span class="card-title">Invalid activation link!!</span>
        <p>
          Oops! There were issues with the activation link, it was highly
          perceived to have been used before... Please, consider requesting for
          an
          <a
            href="{% url 'accounts:resend_email' %}"
            class="btn waves-effect waves-light"
          >
            activate link resend </a
          >.
        </p>
      </div>
    </div>
  </div>
</div>
<!-- content ends -->
{% endblock content %}

ここで一日としましょう.今度はここから続きます!

アウトロ


お誕生日おめでとう🎂✨🥳🤩.
この記事を楽しんでくださいcontacting me for a job, something worthwhile or buying a coffee ☕ .