Django 인증 커스터마이징하기¶
Django에 포함된 인증은 대부분의 일반적인 경우에서 충분히 좋습니다. 그렇지만 원한다면 Django 기본 인증에서 벗어나셔도 됩니다. 당신의 프로젝트에서 인증에 대한 사항을 커스터마이징하기 위해서는 먼저 시스템이 제공해주는 것 중에 어떤 것이 확장 가능하고 어떤 것이 대체 가능한지 알아야 합니다. 이 문서는 어떻게 인증 시스템을 커스터마이징 할 수 있는지에 대해서 자세히 다룹니다.
:ref:`인증 백엔드 <authentication-backends>`는 user model에 저장된 username과 password이 Django의 디폴트가 아닌 다른 서비스에서 인증되어야 할 경우를 위한 확장가능한 시스템을 제공합니다.
독자는 자신의 모델에 Django의 인증 시스템을 통해 확인할 수 있는 :ref:`맞춤 권한 <custom-permissions>`을 줄 수 있습니다.
독자는 디폴트 User
모델을 :ref:`확장 <extending-user>`하거나 완전히 맞춤화된 모델로 :ref:`대체 <auth-custom-user>`할 수 있습니다.
다른 인증 소스들¶
또 다른 인증 소스가 필요한 순간이 있을 수 있습니다 – 즉, username과 password 또는 인증 메소드의 또 다른 소스.
예를 들어 독자의 회사가 모든 직원의 username과 password를 저장하는 LDAP 설정을 이미 가지고 있을 수 있습니다. 만약 사용자가 계정을 LDAP와 Django기반의 애플리케이션에 분리했다면 그건 네트워크 관리자와 사용자 모두에게 곤란할 것입니다.
따라서 이런 상황을 다루기 위해 Django 인증 시스템은 독자가 다른 인증 소스에 플러그인 할 수 있게 해줍니다. 독자는 Django의 디폴트 데이터베이스 기반 스키마를 오버라이드할 수 있습니다. 또는 독자는 다른 시스템과 협력관계로 디폴트 시스템을 사용할 수 있습니다.
Django에 포함된 인증 백엔드에 관한 정보는 :ref:`인증 백엔드 참조 <authentication-backends-reference>`에서 볼 수 있습니다.
인증 백엔드 명세하기¶
비하인드로 Django는 인증을 확인하는 “인증 백엔드” 리스트를 유지하고 있습니다. 누군가 :func:`django.contrib.auth.authenticate()`를 호출하면 – :ref:`사용자 로그인 방법 <how-to-log-a-user-in>`에서 서술한 것처럼 – Django는 모든 인증 백엔드를 거쳐 인증을 시도합니다. 만약 가장 처음의 인증 메소드가 실패하면 Django는 두번째 것을 시도하고 모든 백엔드가 시도될 때까지 반복합니다.
사용하는 인증 백엔드 리스트는 AUTHENTICATION_BACKENDS
설정에 명세됩니다. 이것은 인증하는 방법을 알고 있는 Python 클래스를 가리키는 Python 경로 이름 리스트 이어야 합니다. 이 클래스들은 독자의 Python 경로의 어디에나 있을 수 있습니다.
디폴트로 AUTHENTICATION_BACKENDS
는 다음과 같이 설정됩니다:
["django.contrib.auth.backends.ModelBackend"]
django 사용자 데이터베이스를 확인하고 내장된 권한을 질의하는 기초 인증 백엔드이다. 속도 제한 메커니즘을 통한 무차별 대입 공격에 대한 보호를 제공하지 않는다. 맞춤 인증 백엔드에서 자체 속도 제한 메커니즘을 구현하거나 대부분의 웹 서버에서 제공하는 메커니즘을 사용할 수 있다.
AUTHENTICATION_BACKENDS
의 순서는 중요합니다. 만약 같은 username과 password가 참인 여러 개의 백엔드가 있으면 Django는 첫번째 참인 곳에서 처리를 멈출 것입니다.
만약 백엔드가 PermissionDenied
예외를 발생시키면, 인증은 즉시 실패할 것입니다. Django는 뒤따르는 백엔드는 확인하지 않습니다.
참고
Once a user has authenticated, Django stores which backend was used to
authenticate the user in the user’s session, and reuses the same backend
for the duration of that session whenever access to the currently
authenticated user is needed. This effectively means that authentication
sources are cached on a per-session basis, so if you change
AUTHENTICATION_BACKENDS
, you’ll need to clear out session data if
you need to force users to re-authenticate using different methods. A
simple way to do that is to execute Session.objects.all().delete()
.
인증 백엔드 작성하기¶
인증 백엔드는 두 가지 필수 메소드를 구현하는 클래스입니다: 인증 메소드 와 관련된 선택적 권한 집합 뿐만 아니라 get_user(user_id)
와 authenticate(request, **credentials)
.
get_user`` 메소드는 user_id
를 반환하는데, 이것은 username, 데이터베이스 ID 또는 무엇이든 될 수 있습니다. 하지만 user object 의 primary key 이어야 하며 사용자 객체를 반환합니다.
authenticate
메소드는 키워드 인자로 request
호출 인자와 자격증명을 가진다. 대부분의 경우, 다음과 같이 나타날 것이다:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
하지만 이것은 또한 다음과 같이 토큰을 인증하는 것일 수 있습니다:
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
어느 것이든 authenticate()
는 자격 증명을 확인하고 이 자격 증명이 참인 경우 이 자격 증명과 매치하는 사용자 객체를 리턴해야 합니다. 만약 참이 아니라면 ``None``을 리턴해야 합니다.
request
는 django.http.HttpRequest 라는 Class 이고 만일 django.contrib.auth.authenticate 함수 (백엔드로 그걸 전달하는) 가 제공되지 않으면 None
일 수 있습니다.
Django 관리자는 Django 사용자 객체 와 단단히 결합되어 있습니다. 관리자를 다루는 가장 좋은 방법은 Django User
객체를 독자의 백엔드 에 존재하는 각 사용자로 생성하는 것입니다. (e.g., 독자의 LDAP 디렉토리 에서 외부 SQL 데이터베이스 등) 사전에 스크립트를 작성해서 하거나 authenticate
메소드가 사용자가 로그인한 첫 순간에 할 수 있습니다.
여기에 여러분의 settings.py
파일에 정의된 username 과 password 로 인증하여 사용자가 첫 번째로 인증할 때 Django User
객체를 생성하는 백엔드 예제가 있습니다:
from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class SettingsBackend(BaseBackend):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""
def authenticate(self, request, username=None, password=None):
login_valid = settings.ADMIN_LOGIN == username
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Create a new user. There's no need to set a password
# because only the password from settings.py is checked.
user = User(username=username)
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
맞춤 백엔드 에서 인증 다루기¶
맞춤 인증 백엔드는 스스로 권한을 제공할 수 있다.
사용자 모델과 관리자는 권환 조회 함수(get_user_permissions()
, get_group_permissions()
, get_all_permissions()
, has_perm()
, has_module_perms()
, 및 with_perm()
)를 이런 함수들을 구현하는 인증 백엔드에 위임할 것이다.
사용자에게 주어진 권한은 모든 백엔드에서 반환되는 전체 권한의 상위 집합일 것입니다. 즉, Django 는 하나의 백엔드를 주는 사용자에게 권한을 줍니다.
만약 백엔드가 has_perm()
이나 has_module_perms()
에서 PermissionDenied
예외가 일어나면 , 인증은 즉시 실패하고 Django 는 뒤따르는 백엔드를 확인하지 않을 것입니다.
백엔드는 다음과 같이 매직 관리자에 대한 권한을 구현할 수 있다.:
from django.contrib.auth.backends import BaseBackend
class MagicAdminBackend(BaseBackend):
def has_perm(self, user_obj, perm, obj=None):
return user_obj.username == settings.ADMIN_LOGIN
위의 예제에서 접근을 허가받은 사용자들에게 완전한 권한을 부여합니다. 연관된 django.contrib.auth.models.User
함수에서 주어진 같은 인자이지만 백엔드 인증 함수는 모두 인자로 익명 사용자일 수도 있는 사용자 객체를 받는다는 것을 명심하세요.
전체 인증 구현은 django/contrib/auth/backends.py`의 ``ModelBackend` 클래스에서 찾을 수 있으며, 디폴트 백엔드이고 대부분의 경우 auth_permission
테이블을 쿼리한다.
익명 사용자를 위한 인증¶
익명 사용자는 인증되지 않은 사용자입니다. 즉 그들은 참인 인증 상세를 제공하지 않습니다. 하지만 그것이 꼭 어떤 것을 하는데 권한을 부여받지 않음을 의미하는 것은 아닙니다. 대부분의 기초 수준에서 많은 웹사이트들이 익명 사용자에게 대부분의 사이트를 둘러볼 것을 허가하고 익명 댓글을 포스팅하는 것을 허가합니다.
Django’s permission framework does not have a place to store permissions for
anonymous users. However, the user object passed to an authentication backend
may be an django.contrib.auth.models.AnonymousUser
object, allowing
the backend to specify custom authorization behavior for anonymous users. This
is especially useful for the authors of reusable apps, who can delegate all
questions of authorization to the auth backend, rather than needing settings,
for example, to control anonymous access.
비활성 사용자를 위한 인증¶
비활성 사용자는 is_active
필드가 False
로 설정되어 있는 사람입니다. ModelBackend
와 RemoteUserBackend
인증 백엔드는 이 사용자들이 인증되는 것을 막습니다. 만약 맞춤 사용자 모델이 is_active
필드를 가지고 있지 않다면, 모든 사용자는 인증이 허락될 것 입니다.
만약 비활성 사용자가 인증하는 것을 허락하고자 하면, 독자는 AllowAllUsersModelBackend
나 AllowAllUsersRemoteUserBackend
를 사용할 수 있습니다.
권한 시스템에서 익명 사용자를 위한 지원은 비활성 인증된 사용자가 할 수는 없지만 익명 사용자는 가능한 시나리오를 포함합니다.
여러분의 백엔드 권한 메소드에서 사용자의 is_active
속성을 테스트 하는 것을 잊지 마세요.
객체 권한 다루기¶
Django의 권한 프레임워크는 비록 코어에 구현은 되어 있지 않지만 객체 권한에 기반를 두고 있습니다. 즉 객체 권한을 확인하면 항상 False``나 빈 리스트를 반환할 것입니다 (확인이 수행됨에 따라서). 인증 백엔드는 인증 메소드와 관련된 각 객체에 ``obj
와 user_obj
키워드 파라미터를 받고 적합한 객체 수준 권한을 반환할 수 있습니다.
맞춤 권한¶
주어진 모델 객체를 위한 맞춤 권한을 생성하기 위해서 permissions
모델 메타 속성 을 사용합니다.
이 예제 Task
모델은 두 가지 맞춤 권한, 즉 애플리케이션 사용자가 Task
인스턴스를 가지고 할 수 있는 것과 할 수 없는 것을 정의합니다.
class Task(models.Model):
...
class Meta:
permissions = [
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
]
이것이 하는 유일한 일은 manage.py migrate <migrate>`(:data:`~django.db.models.signals.post_migrate
시그널에 연결된 권한을 생성하는 함수)를 실행할 때 추가적인 권한을 생성하는 것입니다. 당신의 코드는 애플리케이션의 기능에 사용자가 접근(작업의 상태를 변경하거나 작업을 종료)을 시도할 때 이러한 권한들의 값을 검사할 책임이 있습니다. 위의 예에 이어, 다음은 사용자가 작업을 닫아도 되는지 확인합니다.
user.has_perm("app.close_task")
기존 User
모델 확장하기¶
독자 스스로의 모델을 대체하지 않고 기본 User
모델을 확장시키는 데 두 가지 방법이 있습니다. 만약 여러분이 하고자 하는 변경이 순수히 행동적이고 데이터베이스에 저장되는 것의 변화는 필요하지 않다면 독자는 User
에 기반한 프록시 모델 을 생성할 수 있습니다. 이것은 기본 정렬, 맞춤 매니저 또는 맞춤 모델 메소드를 포함해서 프록시 모델에서 제안된 기능을 허용합니다.
만약 독자가 User
에 관련된 정보를 저장하길 원한다면 OneToOneField
를 추가적인 정보를 위한 필드를 포함하는 모델로 사용할 수 있습니다. 이 일대일 모델은 사이트 사용자에 대한 비인증 관련 정보를 저장할 수도 있기 때문에 종종 프로필 모델로 불립니다. 예를 들어 독자는 Employee 모델을 생성할 수도 있습니다:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
Assuming an existing Employee Fred Smith who has both a User and Employee model, you can access the related information using Django’s standard related model conventions:
>>> u = User.objects.get(username="fsmith")
>>> freds_department = u.employee.department
프로필 모델의 필드를 관리자 사용자 페이지에 추가하기 위해선 InlineModelAdmin
를 독자 앱의 admin.py``에 정의합니다. (예로 우리는 :class:`~django.contrib.admin.StackedInline`를 사용할 것입니다.) 그리고 :class:`~django.contrib.auth.models.User` 클래스로 등록된 ``UserAdmin
클래스에 더합니다.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = "employee"
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = [EmployeeInline]
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
이 프로필 모델은 어떤 방식으로든 특별하지 않습니다. - 이것은 단지 사용자 모델과 일대일 링크를 가지기 위한 Django 모델입니다. 이와 같이 프로필 모델은 사용자가 생성될 때 스스로 생성되지 않고 :attr:`django.db.models.signals.post_save`이 생성 또는 관련 모델을 업데이트하기 위해 적절히 사용될 될 수 있습니다.
관련 모델을 사용하면 추가적인 질의나 관련 데이터를 검색할 수 있습니다. 독자의 필요에 따라 관련 필드를 포함하는 맞춤 사용자 모델은 더 나은 선택이 될 수 있습니다. 하지만 프로젝트 앱에서 기본 사용자 모델의 관계를 유지시키면 추가 데이터베이스 로드를 정당화 할 수 있습니다.
맞춤 User
모델 대체하기¶
몇몇 종류의 프로젝트는 Django의 빌트인 User
모델이 항상 적절하진 않은 인증 요구사항을 가지고 있을 수 있습니다. 예를 들어 어떤 사이트에서는 사용자 이름 대신 이메일 주소를 인증 토큰을 사용하는 것이 더 적합할 수 있습니다.
Django는 독자가 맞춤 모델을 참조하는 AUTH_USER_MODEL
설정값을 제공함으로써 기본 사용자 모델을 오버라이드하도록 할 수 있습니다:
AUTH_USER_MODEL = "myapp.MyUser"
이 점 쌍은 django 어플리케이션의(:setting:`INSTALLED_APPS 에 있어야한다) :attr:`~django.apps.AppConfig.label`와 사용자 모델로 사용하려고 하는 django 모델의 이름이다.
프로젝트를 시작할 때 맞춤 사용자 모델 사용하기¶
만약 독자가 새로운 프로젝트를 시작한다면 기본 User
모델이 독자에게 충분하더라도 맞춤 사용자 모델을 설정할 것을 강력히 추천합니다.이 모델은 기본 사용자 모델과 똑같이 행동하지만 만약 필요한 일이 생긴다면 이후에 모델을 맞추화할 수 있습니다:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
모델에 AUTH_USER_MODEL
을 가리키는 것을 잊지마세요. 이 작업은 마이그레이션을 생성하거나 manage.py migrate
를 처음으로 실행시키기 전에 합니다.
또한 앱의 ``admin.py``에 모델을 등록합니다:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
프로젝트 중간에 맞춤 사용자 모델 변경하기¶
데이터베이스 테이블을 생성한 뒤 AUTH_USER_MODEL
를 변경하는 것은 예를 들면 변경이 외래 키와 다대다 관계에 영향을 주기 때문에 상당히 더 어렵습니다.
이 변경은 자동으로는 불가능하고 수동으로 독자의 스키마를 수정하고 데이터를 이전 사용자 테이블에서 옮기고 몇 가지 마이그레이션을 어떻게해서든 수동으로 적용시킬 필요성이 있습니다. 단계들의 개요는 #25313 에서 보실 수 있습니다.
교환가능한 모델을 위한 Django의 동적 의존성 기능의 한계로 AUTH_USER_MODEL
로부터 참조된 모델은 앱의 첫 번째 마이그레이션에서 생성되어야만 합니다. (보통 0001_initial
로 불리는) 그렇지 않으면 독자는 의존성 이슈를 가지게 될 것입니다.
또한, Django는 동적 의존성으로 인한 의존성 루프를 자동으로 해소해주지 않으므로 마이그레이션을 수행할 때 CircularDependencyError
가 발생할 수 있습니다. 이 오류가 발생하면 사용자 모델에 의존하는 모델들을 두 번째 마이그레이션으로 옮김으로써 루프를 해소해야 합니다(일반적으로 어떻게 이뤄지는지 보고 싶다면 서로에 대해 ForeignKey
를 갖는 두 개의 일반적인 모델을 만들고 makemigrations
가 순환 의존성을 어떻게 다루는지 볼 수 있습니다).
재사용 가능 앱과 AUTH_USER_MODEL
¶
재사용 가능한 앱은 맞춤 사용자 모델을 구현하면 안 됩니다. 하나의 프로젝트는 많은 앱을 사용할 수 있고 맞춤 사용자 모델이 구현된 재사용 가능한 앱은 두 개가 동시에 사용될 수 없습니다. 만약 독자가 앱마다 사용자 정보를 저장할 필요가 있다면 아래에서 처럼 ForeignKey
또는 OneToOneField
를 settings.AUTH_USER_MODEL
로 사용하세요.
User
모델 참조하기¶
만약 독자가 User
를 직접 참조한다면 (예를 들어 외래 키로 참조하듯이), AUTH_USER_MODEL
설정이 다른 사용자 모델로 변경된 프로젝트에서는 코드가 동작하지 않을 것입니다.
-
get_user_model
()¶ Instead of referring to
User
directly, you should reference the user model usingdjango.contrib.auth.get_user_model()
. This method will return the currently active user model – the custom user model if one is specified, orUser
otherwise.독자가 외래 키나 다대다 관계를 사용자 모델에 정의할 때 독자는
AUTH_USER_MODEL
설정을 사용하는 맞춤 모델을 명세해야 합니다. 예를 들어:from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, )
사용자 모델에서 받은 시그널을 연결할 때 독자는
AUTH_USER_MODEL
설정을 사용해 맞춤 모델을 명세해야 합니다. 예를 들면:from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(sender, instance, created, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
일반적으로 임포트 시간에 실행되는 코드의
AUTH_USER_MODEL
설정으로 사용자 모델을 참조하는 것이 가장 쉬운 방법입니다. 하지만 Django가 모델을 임포팅하는 동안에get_user_model()``를 호출하는 것도 가능한 벙법이므로 독자는 이런 식으로 ``models.ForeignKey(get_user_model(), ...)
사용할 수 있습니다.만약 독자의 앱이 여러 개의 사용자 모델로 테스트 되고, 예를 들어
@override_settings(AUTH_USER_MODEL=...)
를 사용해서, 독자가 모듈 레벨의 변수에서``get_user_model()``의 결과를 캐시한다면 독자는 캐시를 지우기 위해setting_changed
시그널을 들을 필요가 있을 수 있습니다. 예를 들면:from django.apps import apps from django.contrib.auth import get_user_model from django.core.signals import setting_changed from django.dispatch import receiver @receiver(setting_changed) def user_model_swapped(*, setting, **kwargs): if setting == "AUTH_USER_MODEL": apps.clear_cache() from myapp import some_module some_module.UserModel = get_user_model()
맞춤 사용자 모델 명세하기¶
커스텀 유저 모델로 프로젝트를 시작할 때 당신의 프로젝트에 알맞는 선택인지 아닌지 고려하는 것을 멈추세요.
하나의 모델에서 모든 사용자 관련된 정보를 유지하는 것은 관련된 모델을 조회하기 위한 추가적인 또는 더욱 복잡한 데이터베이스 쿼리를 필요로 하지 않는다. 반면에, 맞춤 사용자 모델과 관련이 있는 하나의 모델에서 특정한 어플리케이션 사용자 정보 더욱 적합할 수 있다. 각 어플리케이션이 다른 어플리케이션에 의해 충돌하거나 깨질 수 있는 가정없이 자체 사용자 데이터 요구사항을 명시하도록 한다. 인증에 중점을 두고, 사용자 모델을 가능한 간단하게 하고, django가 기대하는 맞춤 사용자 모델과 맞도록 최소 요구사항을 따를 것을 의미한다.
만약 독자가 기본 인증 백엔드를 사용한다면 모델은 인증 목적을 위해 사용될 수 있는 하나의 고유한 필드를 가져야합니다. 이 필드는 사용자 이름, 이메일 주소, 또는 다른 고유한 속성도 가능합니다. 고유하지 않은 사용자 이름 필드는 독자가 이것을 지원할 수 있는 맞춤 인증 백엔드를 사용한다면 허용됩니다.
올바른 맞춤 사용자 모델을 만드는 가장 쉬운 방법은 AbstractBaseUser
를 상속하는 것입니다. AbstractBaseUser
은 해쉬 비밀번호와 토큰화된 비밀번호 재설정을 포함해서 사용자 모델의 핵심적인 구현을 제공합니다. 독자는 몇 가지 키 구현을 상세하게 제공해야 합니다.
-
class
models.
CustomUser
¶ -
USERNAME_FIELD
¶ A string describing the name of the field on the user model that is used as the unique identifier. This will usually be a username of some kind, but it can also be an email address, or any other unique identifier. The field must be unique (e.g. have
unique=True
set in its definition), unless you use a custom authentication backend that can support non-unique usernames.다음 예에서
identifier
필드가 식별 필드로 사용됩니다.class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = "identifier"
-
EMAIL_FIELD
¶ “User” model 에서 이메일 필드를 이름을 묘사하는 스트링. 이 값은 “models.AbstractBaseUser.get_email_field_name” 라는 메서드로 얻을수 있습니다.
-
REQUIRED_FIELDS
¶ djadmin ( = django-admin ) 의 createsuperuser 명령으로 user 를 만들때 입력대기 (prompted) 되는 필드 이름들 목록. user 는 이들 필드들이 입력 되도록 합니다. 그것은 다음과 같은 필드들을 포함 시킵니다. 속성이 django.db.models.Field.blank 는 “False” 혹은 “undefined” 로 , 그리고 아마도 다른 필드들은 user 가 생성될때 상호작동 방식으로 입력 되도록 포함 될 수도 있습니다.
예를 들어, 여기에 값이 제공되어야 하는 두개의 필드 - 생년월일, 키 - 를 갖는 User Model 에 대한 부분적인 정의(definition) 를 하였습니다.
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ["date_of_birth", "height"]
참고
요구되는 필드들(Required Fields) 는 여러분의 user model 에서 요구되는 필드의 모두 포함합니다만 “USERNAME_FIELD” 혹은 “Password” 필드는 제외합니다. 이들은 항상 입력이 되도록 하기 때문입니다.
-
is_active
¶ 사용자가 “active”로 간주되는지를 나타내는 불리언 속성이다. 이 속성은 ``AbstractBaseUser``에 대한 속성으로 디폴트로 “True”가 제공된다. 구현 방법은 선택된 인증 백엔드의 세부 세항에 따라 달라질 것이다. 세부 사항은
is_active attribute on the built-in user model
문서를 참조하라.
-
get_full_name
()¶ 선택 가능한. 사용자의 풀네임과 같은 더 긴 공식 식별자이다. 구현된다면, :mod:`django.contrib.admin`의 객체의 히스토리에 사용자명과 나란히 나타난다.
-
get_short_name
()¶ 선택 가능한. 사용자의 성과 같은 짧은 비공식 식별자이다. 구현된다면, 사용자명을 :mod:`django.contrib.admin`의 헤더에 있는 사용자에게 보내는 인사말로 치환하다.
“AbstractBaseUser`”를 임포트
AbstractBaseUser``과 ``BaseUserManager``는 ``django.contrib.auth.base_user
에서 가져올 수 있어서INSTALLED_APPS
.에 있는 ``django.contrib.auth``를 포함하지 않고 임포트될 수 있다.-
다음 속성과 메소드는 AbstractBaseUser
:의 모든 서브클래스에서 이용할 수 있다.
-
class
models.
AbstractBaseUser
¶ -
get_username
()¶ “USERNAME_FIELD”에 의해 지명된 필드의 값을 반환합니다.
-
clean
()¶ :meth:`normalize_username`을 호출함으로써 사용자명을 정규화한다. 이 메소드를 오버라이드하면, 정규화를 유지하기 위해 ``super()``을 호출해야 한다.
-
classmethod
get_email_field_name
()¶ EMAIL_FIELD
속성으로 명시된 이메일 필드의 이름을 리턴한다.'email'
if ``EMAIL_FIELD``의 디폴트값은 명시되지 않았다.
-
classmethod
normalize_username
(username)¶ 다른 유니코드의 코드 포인트를 가진 시각적으로 동일한 문자가 동일한 것으로 간주되도록 NFKC 유니코드 정규화를 사용자명에 적용하다.
-
is_authenticated
¶ 항상 ``True``(항상 ``False``인 `AnonymousUser.is_authenticated``와 반대이다) 인 읽기 전용 속성. 이는 사용자가 인증되었는지를 알려주는 방법이다. 모든 권한을 의미하는 것은 아니고 사용자가 활성 상태인지 유효한 세션이 있는지를 확인하는 것이 아니다. 그럼에도 불구하고, :class:`~django.contrib.auth.middleware.AuthenticationMiddleware (현재 로그인되어 있는 사용자를 나타낸다) 에 의해 덧붙여졌는지 알아내기 위해 ``request.user``의 속성을 확인할 것이고, 이 속성은 모든
User
instance에 대해 ``True``임을 알아야 한다.
-
is_anonymous
¶ 항상 ``False``인 읽기 전용 속성이다.
AnonymousUser
객체를 차별화하는 방법이다. 일반적으로, 이 속성에 대해서는is_authenticated
이용하는 것이 좋다.
-
set_password
(raw_password)¶ 패스워드 해싱을 처리하여, 주어진 원시 문자열로 사용자의 패스워드를 설정한다.
AbstractBaseUser
객체를 저장하지 않는다.raw_password가 ``None``이면, 마치
set_unusable_password()
이 사용된 것처럼, 패스워드는 사용할 수 없는 패스워드로 설정한다.
-
set_unusable_password
()¶ 사용자가 패스워드를 설정하지 않은 것으로 표시한다. 빈 문자열을 패스워드로 가지는 것과 다르다.
check_password()
의 사용자에 대해 ``True``를 절대 리턴하지 않을 것이다.AbstractBaseUser
객체를 저장하지 않는다.애플리케이션에 대한 인증이 LDAP 디렉토리와 같은 기존 외부 소스에 대해 수행되는 경우 필요할 수 있다.
-
has_usable_password
()¶ 이 사용자에 대해
set_unusable_password()
가 호출되면, ``False``를 리턴한다.
-
get_session_auth_fallback_hash
()¶ - New in Django 4.1.8.
Yields the HMAC of the password field using
SECRET_KEY_FALLBACKS
. Used byget_user()
.
-
AbstractUser
하위 클래스 AbstractBaseUser
:
사용자 맞춤 모델을 위한 관리자 작성¶
사용자 모델에 대한 맞춤 관리자를 정의해야 한다. 사용자 모델이 username
, email
, is_staff
, is_active
, is_superuser
, last_login
및 date_joined
필드를 django의 디폴드 사용자와 동일하게 정의한다면, django의 UserManager
를 설치할 수 있다. 그러나, 사용자 모델이 다른 필드를 정의하면, 두 개의 추가적인 메소드를 제공하는 :class:`~django.contrib.auth.models.BaseUserManager`를 상속하는 맞춤 관리자를 정의해야한다.
-
class
models.
CustomUserManager
¶ -
create_user
(username_field, password=None, **other_fields)¶ ``create_user()``의 프로토 타입은 사용자명 필드와 모든 필수 필드를 인자로 허용해야 한다. 예를 들어, 사용자 모델이 ``email``을 사용자명 필드로 이용한다면, ``date_of_birth``를 필수 필드로 가지고, ``create_user``는 정의되어야 한다.
def create_user(self, email, date_of_birth, password=None): # create user here ...
-
create_superuser
(username_field, password=None, **other_fields)¶ ``create_superuser()``의 프로토 타입은 사용자명 필드와 모든 필수 필드를 인자로 허용해야 한다. 예를 들어, 사용자 모델이 ``email``을 사용자명 필드로 이용한다면, ``date_of_birth``를 필수 필드로 가지고, ``create_superuser``는 정의되어야 한다.
def create_superuser(self, email, date_of_birth, password=None): # create superuser here ...
-
For a ForeignKey
in USERNAME_FIELD
or
REQUIRED_FIELDS
, these methods receive the value of the
to_field
(the primary_key
by default) of an existing instance.
:class:`~django.contrib.auth.models.BaseUserManager`는 다음의 유틸리티 메소드를 제공한다.
-
class
models.
BaseUserManager
¶ -
classmethod
normalize_email
(email)¶ 이메일 주소의 도메인 부분을 소문자 표기를 함으로써 이메일 주소를 정규화한다.
-
classmethod
Django의 기본 “User”를 확장¶
If you’re entirely happy with Django’s User
model, but you want to add some additional profile information, you could
subclass django.contrib.auth.models.AbstractUser
and add your custom
profile fields, although we’d recommend a separate model as described in
맞춤 사용자 모델 명세하기. AbstractUser
provides the full
implementation of the default User
as an
abstract model.
사용자 맞춤과 내장된 폼¶
django의 내장된 forms 1 와 :ref:`forms 2`는 작업중인 사용자 모델에 대한 특정한 가정을 한다.
다음 폼은 AbstractBaseUser
:의 모든 서브 클래스와 양립할 수 있다.
AuthenticationForm
: :attr:`~models.CustomUser.USERNAME_FIELD`에 의해 명시되는 사용자명 필드를 이용한다SetPasswordForm
PasswordChangeForm
AdminPasswordChangeForm
다음 폼은 사용자 모델에 대한 가정을 하고 해당 가정이 충족되는 경우일 때, 있는 그대로로 사용된다.:
PasswordResetForm
: 사용자 모델이 비활성화 사용자의 패스워드를 재설정 하는 것을 막기 위해 사용자와is_active``의 불리언 필드를 식별하고 :meth:`~models.AbstractBaseUser.get_email_field_name`(`email
의 디폴트값) 에 의해 리턴된 이름과 사용자의 이메일 주소를 저장하는 필드를 가지는 것으로 가정한다.
마지막으로, 다음의 폼은 :class:`~django.contrib.auth.models.User`와 연결되어 있으며 사용자 맞춤 모델을 사용하기 위해서는 다시 작성되거나 상속되어야 한다.
사용자 맞춤 모델이 ``AbstractUser``의 서브 클래스일 경우, 다음과 같은 방식으로 이 폼을 상속할 수 있다.
from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields + ("custom_field",)
In older versions, UserCreationForm
didn’t save many-to-many form fields for a custom user model.
커스텀 User 와 django.contrib.admin 모듈¶
커스텀 User Model 이 admin 에서도 작동 되길 원하면 추가적인 속성과 매서드를 정의해야 합니다. 이들 매서드들은 Admin 으로 하여금 admin content 에 그 User Model 을Access 할 수 있게 합니다.
-
class
models.
CustomUser
-
is_staff
¶ 그 user 가 admin site 에 접근 가능하다면 “True” 를 반환합니다
-
is_active
¶ 그 user 가 지금 현재 “active” 하다면 “True” 를 반환합니다
-
has_perm(perm, obj=None):
사용자에게 명명된 권한이 있다면 ``True``를 리턴한다. 만일 ``obj``가 제공되면, 권한은 특정한 객체 인스턴스에 대해서 확인되어야한다.
-
has_module_perms(app_label):
사용에게 주어진 어플리케이션에서 모델에 액세스할 권한이 있다면 ``True``를 리턴한다.
관리자에게 사용자 맞춤 모델을 등록해야 할 것이다. 사용자 맞춤 모델이 django.contrib.auth.models.AbstractUser``을 상속한다면, django의 기존 ``django.contrib.auth.admin.UserAdmin
클래스를 사용할 수 있다. 그러나, 사용자 모델이 AbstractBaseUser`을 상속한다면, ``ModelAdmin`
맞춤 클래스를 정의해야 할 것이다. ``django.contrib.auth.admin.UserAdmin``의 디폴트를 서브클래스화 하는 것을 가능하게 할 것이나, 사용자 맞춤 클래스에 없는 ``django.contrib.auth.models.AbstractUser``의 필드를 참조하는 모든 정의를 오버라이드 해야할 것이다.
참고
django.contrib.auth.admin.UserAdmin``의 서브 클래스인 맞춤 ``ModelAdmin
을 사용한다면, ``fieldsets``(사용자를 편집하는 데에 사용되는 필드)와 ``add_fieldsets``(사용자 생성에 사용되는 필드)에 맞춤 필드를 추가해야한다. 예를 들면, 다음과 같다:
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
...
fieldsets = UserAdmin.fieldsets + ((None, {"fields": ["custom_field"]}),)
add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ["custom_field"]}),)
자세한 사항은 :ref:`a full example 1`을 확인하라.
사용자 커스터마이징 과 권한들¶
django의 권한 프레임워크를 자체 사용자 클래스에 쉽게 포함시키기 위해, django는 :class:`~django.contrib.auth.models.PermissionsMixin`를 제공한다. 이는 사용자 모델에 클래스 계층 구조를 포함할 수 있는 추상 모델이며, django의 권한 모델을 지원하는 데에 필요한 모든 메소드와 데이터베이스 필드를 제공할 수 있다.
다음의 메소드와 속성으로 PermissionsMixin
을 제공한다.
-
class
models.
PermissionsMixin
¶ -
is_superuser
¶ 불리언. 명시적으로 할당하지 않고 모든 권한을 사용자가 갖도록 지정한다.
-
get_user_permissions
(obj=None)¶ 사용자가 직접적으로 갖는 권한 문자열의 모음을 리턴한다
obj
가 전달이 되면, 특정한 해당 객체에 대한 사용자 권한만 리턴한다.
-
get_group_permissions
(obj=None)¶ 그룹을 통해 사용자가 갖는 권한 문자열의 모음을 리턴한다.
obj
가 전달이 되면, 특정한 해당 객체에 대한 그룹 권한만 리턴한다.
-
get_all_permissions
(obj=None)¶ 그룹과 사용자 권한을 통해 사용자가 갖는 권한 문자열의 모음을 리턴한다.
obj
가 전달이 되면, 특정한 해당 객체에 대한 권한만 리턴한다.
-
has_perm
(perm, obj=None)¶ 특정한 권한을 사용자가 갖는다면,``True`` 를 리턴하고,
perm``은 ``"1.2"
안의 포맷이다(permissions 3 참조). :attr:. User.is_active` 및is_superuser
가 모두 ``True``이면, 이 메소드는 항상 ``True``를 리턴한다.obj
가 전달이 되면, 이 메소드는 모델에 대한 권한이 아니라 특정 객체에 대한 권한을 체크한다.
-
has_perms
(perm_list, obj=None)¶ 사용에게 각각의 특정한 권한이 주어진다면 , ``True``를 리턴하고, 각각의 perm은 ``”1.2” 안의 포맷이다.
User.is_active
및is_superuser
가 모두 `True``이면, 이 메소드는 항상 ``True``를 리턴한다.obj
가 전달이 되면, 이 메소드는 모델에 대한 권한이 아니라 특정 객체에 대한 권한을 체크한다.
-
has_module_perms
(package_name)¶ 사용자가 주어진 패키지(django 애플리케이션 레이블)에 대한 모든 권한을 가지고 있으면, ``True``를 리턴한다. 만일,
User.is_active
및is_superuser
모두 ``True``라면, 이 메소드는 항상 ``True``를 리턴한다.
-
사용자 커스터마이즈와 프록시 모델¶
One limitation of custom user models is that installing a custom user model
will break any proxy model extending User
.
Proxy models must be based on a concrete base class; by defining a custom user
model, you remove the ability of Django to reliably identify the base class.
If your project uses proxy models, you must either modify the proxy to extend
the user model that’s in use in your project, or merge your proxy’s behavior
into your User
subclass.
전체 예시¶
Here is an example of an admin-compliant custom user app. This user model uses
an email address as the username, and has a required date of birth; it
provides no permission checking beyond an admin
flag on the user account.
This model would be compatible with all the built-in auth forms and views,
except for the user creation forms. This example illustrates how most of the
components work together, but is not intended to be copied directly into
projects for production use.
This code would all live in a models.py
file for a custom
authentication app:
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError("Users must have an email address")
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, date_of_birth, password=None):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name="email address",
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["date_of_birth"]
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
Then, to register this custom user model with Django’s admin, the following
code would be required in the app’s admin.py
file:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from customauth.models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(
label="Password confirmation", widget=forms.PasswordInput
)
class Meta:
model = MyUser
fields = ["email", "date_of_birth"]
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = MyUser
fields = ["email", "password", "date_of_birth", "is_active", "is_admin"]
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ["email", "date_of_birth", "is_admin"]
list_filter = ["is_admin"]
fieldsets = [
(None, {"fields": ["email", "password"]}),
("Personal info", {"fields": ["date_of_birth"]}),
("Permissions", {"fields": ["is_admin"]}),
]
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = [
(
None,
{
"classes": ["wide"],
"fields": ["email", "date_of_birth", "password1", "password2"],
},
),
]
search_fields = ["email"]
ordering = ["email"]
filter_horizontal = []
# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
Finally, specify the custom model as the default user model for your project
using the AUTH_USER_MODEL
setting in your settings.py
:
AUTH_USER_MODEL = "customauth.MyUser"