トークンを生成し、トークンの登録を確認
"token": "e2db80303283ee15f102789e2e7f5a4053cb774041b385a83eeab19fc6298aee"
token_key は検索用のインデックスであり、ハッシュ化されていないトークンの頭文字8文字である
先ほど発行したトークンの頭文字8文字と一致していることを確認
token_key: e2db8030
knox model の説明
knox.models.py
class AbstractAuthToken(models.Model):
objects = AuthTokenManager()
digest = models.CharField(
max_length=CONSTANTS.DIGEST_LENGTH, primary_key=True)
token_key = models.CharField(
max_length=CONSTANTS.MAXIMUM_TOKEN_PREFIX_LENGTH +
CONSTANTS.TOKEN_KEY_LENGTH,
db_index=True
)
user = models.ForeignKey(User, null=False, blank=False,
related_name='auth_token_set', on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
expiry = models.DateTimeField(null=True, blank=True)
class Meta:
abstract = True
def __str__(self) -> str:
return f'{self.digest} : {self.user}'
class AuthToken(AbstractAuthToken):
class Meta:
swappable = 'KNOX_TOKEN_MODEL'
それぞれのフィールドについて詳しく説明させていただきます:
1. digest
digest = models.CharField(max_length=CONSTANTS.DIGEST_LENGTH, primary_key=True)
- トークンのハッシュ値を保存するフィールドです
-
primary_key=True
で主キーとして設定されています - セキュリティのため、実際のトークン値ではなく、ハッシュ化された値を保存します
- CharFieldとして定義され、長さは
CONSTANTS.DIGEST_LENGTH
で制限されています
2. token_key
token_key = models.CharField(
max_length=CONSTANTS.MAXIMUM_TOKEN_PREFIX_LENGTH + CONSTANTS.TOKEN_KEY_LENGTH,
db_index=True
)
- トークンの識別子部分を保存するフィールドです
-
db_index=True
でインデックスが作成され、検索を高速化します - 最大長は
MAXIMUM_TOKEN_PREFIX_LENGTH
とTOKEN_KEY_LENGTH
の合計です - トークンの一部を保存することで、完全な照合の前に高速なフィルタリングが可能になります
3. user
user = models.ForeignKey(User, null=False, blank=False,
related_name='auth_token_set',
on_delete=models.CASCADE)
- トークンの所有者であるユーザーへの外部キー参照です
-
null=False, blank=False
で必須フィールドとして設定されています -
related_name='auth_token_set'
により、ユーザーモデルからauth_token_set
でトークンにアクセス可能です -
on_delete=models.CASCADE
でユーザーが削除された場合、関連するトークンも削除されます
4. created
created = models.DateTimeField(auto_now_add=True)
- トークンが作成された日時を自動的に記録します
-
auto_now_add=True
により、レコード作成時に自動的に現在時刻が設定されます
5. expiry
expiry = models.DateTimeField(null=True, blank=True)
- トークンの有効期限を設定するフィールドです
-
null=True, blank=True
で任意フィールドとして設定されています - 値が設定されている場合、その日時でトークンは無効になります
このモデルはAbstractAuthToken
として抽象クラスで定義され、実際の実装はAuthToken
クラスで行われています。swappable = 'KNOX_TOKEN_MODEL'
の設定により、必要に応じて独自のトークンモデルに置き換えることが可能です。
knox.models.py
class AuthTokenManager(models.Manager):
def create(
self,
user,
expiry=knox_settings.TOKEN_TTL,
prefix=knox_settings.TOKEN_PREFIX,
**kwargs
):
token = prefix + crypto.create_token_string()
digest = crypto.hash_token(token)
if expiry is not None:
expiry = timezone.now() + expiry
instance = super().create(
token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH], digest=digest,
user=user, expiry=expiry, **kwargs)
return instance, token
これはDjango Knoxライブラリの認証トークンを作成するためのマネージャークラスです。詳しく説明させていただきます:
AuthTokenManagerの機能説明
def create(self, user, expiry=knox_settings.TOKEN_TTL, prefix=knox_settings.TOKEN_PREFIX, **kwargs):
このメソッドは以下の主要なパラメータを受け取ります:
-
user
: トークンを発行するユーザー -
expiry
: トークンの有効期限(デフォルトはknox_settings.TOKEN_TTL
) -
prefix
: トークン文字列の接頭辞(デフォルトはknox_settings.TOKEN_PREFIX
)
処理の流れ
- トークンの生成:
token = prefix + crypto.create_token_string()
- 設定された接頭辞と、暗号学的に安全な乱数文字列を組み合わせて新しいトークンを生成します
- ダイジェストの作成:
digest = crypto.hash_token(token)
- セキュリティのため、トークンをハッシュ化します
- このハッシュ値がデータベースに保存されます
- 有効期限の設定:
if expiry is not None:
expiry = timezone.now() + expiry
- 有効期限が指定されている場合、現在時刻に指定された期間を加算します
- トークンインスタンスの作成:
instance = super().create(
token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH],
digest=digest,
user=user,
expiry=expiry,
**kwargs
)
- データベースにトークン情報を保存します
-
token_key
には完全なトークンの一部のみを保存(検索用) -
digest
にはトークン全体のハッシュ値を保存 - ユーザーと有効期限情報も保存
- 戻り値:
return instance, token
- データベースに保存されたトークンインスタンスと
- クライアントに送信する生のトークン文字列を返します
セキュリティ上の特徴
- 完全なトークン文字列はデータベースに保存されません
- データベースには検索用の部分文字列(token_key)とハッシュ値(digest)のみが保存されます
- トークンの検証時には、受け取ったトークンをハッシュ化して保存されているダイジェストと比較します
参考文献: