見出し画像

TypeScriptで書かれている認証機能をPythonで実装した話

はじめに

Webアプリを作るのに、認証やログイン機能がむずかしいと思う人が多いと思います。また、便利なAPIが出てきて中でどんな感じで処理しているか気になる人も多いと感じます。

ですので、自分で書きながら解説していきたいと思います!

参考サイト

【連載】 ログイン機能を作ってみよう


どうやって作っていくの?

基本的に以下の処理とデータベースを設計すれば作れます

1.暗号化、復号化、ハッシュ値を生成する関数を用意
2.関数を使い、文字列が暗号化できるか、パスワードが照合できるかをためす。
3.データベースにテーブルを用意してサインアップ、ログイン処理を書く

データベースは以下のように設計しましょう

ユーザーテーブル
id、パスワード、ソルト

4.サインアップとログインの関数にセッションを生成する関数を用意する

テーブルをこの時追加しましょう

セッションテーブル
id、作成日時、トークン

後は、用意した関数をフレームワーク上で呼び出せば完成です。

暗号化と復号化

import hashlib
import Crypto.Cipher.AES as AES
import uuid
import base64

def encrypt(plainText):
   if(plainText == ""):
       return 0

   aes = AES.new(setting.key,AES.MODE_EAX,setting.iv)

   cipherText = aes.encrypt(plainText.encode("utf-8"))

   return base64.b64encode(cipherText)

def decrypto(cipherText):
   cipherText = base64.b64decode(cipherText)
   
   if(cipherText == ""):
       return 0
   de = AES.new(setting.key,AES.MODE_EAX,setting.iv)

   plainText = de.decrypt(cipherText)

   return plainText.decode("utf-8")

まず、importの部分からみていきます

hashlib ハッシュ化を行うライブラリ。パスワードはハッシュ化しようね
Crypto.Cipher ユーザーの情報を暗号化する。データベースから漏れた時の被害を減らすため
uuid 固有のidを生成します。トークンの生成なんかを行います
base64 暗号化した文字列をデータベースに保存するために使います。そのままデコードするとエラーが出るのでそれを防ぐためです

基本的な処理は同じなので言語ごとに上の機能を持ったライブラリを使えば、実装できるとおもいます

暗号化をする際には、keyと初期ベクトルが必要です。
どちらも文字列などを.envで設定してencodeしたものを使いましょう。

ハッシュ化

def makeHash(data,salt):
   data = data + salt
   result = hashlib.sha256(data.encode()).hexdigest()
   return result

ハッシュ化です。引数を見てみましょう。
dataはハッシュ化に使いたい文字列です。saltは文字列にくつけるランダムな文字列です。ハッシュは文字列とアルゴリズムで生成されるハッシュ値が決まるのでソルトを合わせて特定されにくくします。

サインアップ

def signup(email="",password=""):
   if(email is "" or password is ""):
       return 0
   
   emailEncrypt = encrypt(email)
   fdb = fireDB.Firebase(setting.cred,setting.option)
   
   ref = fdb.getRef("users/" + emailEncrypt.decode())
   user = fdb.getData(ref)
   if(user is not None):
       fdb.close()
       del fdb
       return 1
   salt = str(uuid.uuid4())
   result = {
       "id": str(uuid.uuid4()),
       "salt": salt,
       "password": makeHash(password,salt)
   }
   fdb.insert(ref,result)
   fdb.close()
   del fdb
   token = session.makeSession(result["id"])
   return token

まず、emailとパスワードが入力されているかを確認します。次に送られてきたemail(ユーザー名でもOK)などを暗号化し、データベースに他のユーザーが既に登録していないかをuser is not Noneで確認します。つぎにソルトを生成してパスワードのハッシュ化とsaltを保存します。これでユーザーの生成は完了です。

ログイン、ログアウト

def login(email = "",password = ""):
   if(email is "" or password is ""):
       return 0
   emailEncrypt = encrypt(email)
   fdb = fireDB.Firebase(setting.cred,setting.option)
   
   ref = fdb.getRef("users/" + emailEncrypt.decode())
   user = fdb.getData(ref)
   fdb.close()
   del fdb
   if(user is None):
       return 1
   if(user["password"] != makeHash(password,user["salt"])):
       return 1
   else:
       return session.makeSession(user["id"])

def logout(userId = "",token = ""):
   if(userId == "" or token == ""):
       return 0
   session.revokeSession(userId,12)
   

次に登録したユーザーでログインをします。サインアップと同様フォームの中身が空の場合はエラーを出してあげます。

次にemailを暗号化し、データベースからユーザーの情報を取り出します。ユーザーがいない場合のエラー処理とパスワードの照合をif文で行いましょう

セッション

def revokeSession(userId,exp):
   date = datetime.datetime.now()
   hour = date.hour - exp
   if(hour < 0):
       date = date.replace(day = date.day - 1)
       date = date.replace(hour = 24 + hour)
   fdb = fireDB.Firebase(setting.cred,setting.option)
   ref = fdb.getRef("session").child(userId)
   res = fdb.getData(ref)
   
   if(res is not None):
       ref.delete()
   
   fdb.close()
   del fdb
   
def makeSession(userId):
  
   result = {
       "token": str(uuid.uuid4()),
       "create": str(datetime.datetime.now())
   }
   revokeSession(userId,12)
   
   fdb = fireDB.Firebase(setting.cred,setting.option)
   ref = fdb.getRef("session/" + userId)
   
   fdb.insert(ref,result)
   res = fdb.getData(ref)
   
   res["userId"] = userId
   
   fdb.close()
   del fdb

   return res

セッションでは、以下の処理を行いましょう。

1.過去のセッションを削除する。ここではrevokeSessionを呼び出します。

2.トークンと作成日時をセッションテーブルに保存します。userIdとトークンの配列を返却します。この返却値をフレームワークのクッキーやセッションを扱うライブラリで保存してください。


さいごに

基本的なユーザーの会員機能をこれで実装できました。

今回はPythonを使いましたが、基本的に同じライブラリを使えば、どの言語でも実装が可能です。
これで、アプリをももっと魅力的にできる人が増えたらと思います

この記事が気に入ったらサポートをしてみませんか?