API에 대한 액세스 제어를 추가하고 API를 추가하여 사용자를 만들고 인증합니다.
현재 우리 API는 완전히 open상태인데요. 누구나 모든 것을 만들고 액세스하고 삭제할 수 있습니다. 이러한 액세스 제어를 추가하려고합니다.
poll
or list of polls
에 접근 가능.poll
create 가능choice
create 가능poll
에 대해서만 choice
생성 가능poll
을 만든 인증된 유저가 delete 가능access control을 가능하게 하기 위해서 API 2개를 추가할게요.
/users/
엔드포인트 - 유저 생성 API/login/
엔드포인트 - 유저를 확인하고 토큰 발급유저 시리얼라이저를 추가할텐데요. creating기능을 허용해줍니다.
# ...
from django.contrib.auth.models import User
# ...
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
User
인스턴스를 저장하는 ModelSerializer 메소드인 create()
를 오버라이딩 할게요.
hash한 비밀번호로 저장하게 하기위해서 user.set_password()
를 사용하기도 했어요.
extra_kwargs = { 'password': { 'write_only': True}}
를 사용하여 해당 응답으로 비밀번호를 찾지는 않게 할게요.
유저 생성을 위해서 UserSerializer
를 뷰에 더하도록 할게요. 그리고 뷰에서 구현되면 urls.py에 연결하도록 하겠습니다.
# ...
from .serializers import PollSerializer, ChoiceSerializer, VoteSerializer, UserSerializer
# ...
class UserCreate(generics.CreateAPIView):
serializer_class = UserSerializer
# in urls.py
# ...
from .apiviews import PollViewSet, ChoiceList, CreateVote, UserCreate
urlpatterns = [
# ...
path("users/", UserCreate.as_view(), name="user_create"),
]
/users/
에 json형식으로 작성한 API를 테스팅 할수 있어요.
{
"username": "nate.silver",
"email": "nate.silver@example.com",
"password": "FiveThirtyEight"
}
응답을 아래와 같이 주게 되요.
{
"username": "nate.silver",
"email": "nate.silver@example.com"
}
동일한 데이터를 다시 입력하게되면 아래와 같이 오류를 뱉어네요.
{
"username": [
"A user with that username already exists."
]
}
Django Rest Framework를 사용하면 DEFAULT_AUTHENTICATION_CLASSES를 사용하여 모든 뷰에 적용되는 기본 인증 체계를 설정할 수 있습니다.
토큰 인증을 사용 할건데요. settings.py에서 추가하도록 할게요.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
또한 rest_framework.authtoken앱 을 활성화해야 하므로 INSTALLED_APPSsettings.py에서 업데이트 하십시오.
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
python manage.py migrate # 테이블 생성도 하겠습니다. 방금 입력한 앱의 테이블을 만들어 주는거에요. 토큰 값을 저장하게되요.
REST_FRAMEWORK = {
# ...
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
또한 UserCreate
뷰에 예외를 부여하는 하기 위해 전역 설정이 아래와 같이 필요해요.
class UserCreate(generics.CreateAPIView):
authentication_classes = ()
permission_classes = ()
serializer_class = UserSerializer
UserCreate
뷰에 global authentication scheme 시키지 않기 위해 제외하려면 authentication_classes = ()
및 permission_classes = ()
를 클래스 변수로 정의해줘야해요.
UserCreate
뷰에서 사용자가 생성 될 때 토큰이 생성되도록하고 싶으므로 UserSerializer
를 업데이트합니다.
from rest_framework.authtoken.models import Token
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
Token.objects.create(user=user)
return user
settings.py
에 rest_framework.authentication.TokenAuthentication
을 추가 했으므로. Authorization: Token c2a84953f47288ac1943a3f389a6034e395ad940
을 헤더
에 넣어줘야해요. 를 auhenticate로 설정해야합니다.
사용자가 사용자 이름과 비밀번호를 제공하고 토큰을 되 찾을 수있는 API가 필요합니다. - 로그인 API
이 API를 사용하여 토큰을 저장하지 않기 때문에 serializer를 추가하지 않을 것입니다.
# in apiviews.py
# ...
from django.contrib.auth import authenticate
class LoginView(APIView):
permission_classes = ()
def post(self, request,):
username = request.data.get("username")
password = request.data.get("password")
user = authenticate(username=username, password=password)
if user:
return Response({"token": user.auth_token.key})
else:
return Response({"error": "Wrong Credentials"}, status=status.HTTP_400_BAD_REQUEST)
# in urls.py
# ...
from .apiviews import PollViewSet, ChoiceList, CreateVote, UserCreate, LoginView
urlpatterns = [
path("login/", LoginView.as_view(), name="login"),
# ...
]
참고로 /user/
엔드포인트를 지금 당장 사용할수 없습니다. 회원가입을 통해서 토큰 생성을 해야하는데 아직 토큰을 발급 받은 유저가 없조?
만약 로그인을 시도한다고 해도 "User has no auth_token" 오류가 발생해요.
/user/
엔드 포인트를 이용해서 회원가입시 토큰이 발생된 유저를 만들도록 할게요.
httpie
를 통해서 POST요청을 보내게 되면 아래와 같아요.
❯ http POST http://localhost:8000/login/ 'username=korean' 'password=11111111'
HTTP/1.1 200 OK
Allow: POST, OPTIONS
Content-Length: 52
Content-Type: application/json
Date: Sat, 20 Feb 2021 15:36:48 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.1
Vary: Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"token": "d8306c4de9cb05c3c6da1f5332a965be3b9ac7b1"
}
올바르게 POST요청을 보내게 된다면 아래와 같은 토큰 값을 받게되요.
{
"token": "c300998d0e2d1b8b4ed9215589df4497de12000c"
}
다른 방법으로 간다면 obtain_auth_token
메서드를 이용할 수 있어요.
urlpatterns = [
...
path("api-token-auth/", views.obtain_auth_token, name="login"),
]
아래와 같이 httpie를 이용해서 접근해보세요. 그럼 토큰값을 반환 받게 되요.
http post http://127.0.0.1:8000/api-token-auth/ username=vitor password=123
/polls/
엔드포인트에 헤더없이 API에 액세스 해보십시오 . 아래와 같은 http 상태 코드로 오류가 발생 합니다. HTTP 401 Unauthorized
{
"detail": "Authentication credentials were not provided."
}
결국 HTTP헤더에는 해당 Authorization: Token <your token>
형식을 갖춘 키와 값이 있어야해요.
PollViewSet.destroy
and ChoiceList.post
를 오버라이딩하여 재정의할게요.
하나는 뷰셋이고 나머지는 APIView조!?
# ...
from rest_framework.exceptions import PermissionDenied
class PollViewSet(viewsets.ModelViewSet):
# ...
def destroy(self, request, *args, **kwargs):
poll = Poll.objects.get(pk=self.kwargs["pk"])
if not request.user == poll.created_by:
raise PermissionDenied("You can not delete this poll.")
return super().destroy(request, *args, **kwargs)
class ChoiceList(generics.ListCreateAPIView):
# ...
def post(self, request, *args, **kwargs):
poll = Poll.objects.get(pk=self.kwargs["pk"])
if not request.user == poll.created_by:
raise PermissionDenied("You can not create choice for this poll.")
return super().post(request, *args, **kwargs)
둘다 request.user
를 확인하게되요. 그리고 PermissionDenied
에러를 발생시킬 수 있게 코딩도 되어있어요.
다른 유저의 poll을 DELETE메서드를 이용해서 확인할수 있어요. 정상적으로 되었다면 HTTP 403 Forbiden
을 응답으로 뱉어내야해요.
{
"detail": "You can not delete this poll."
}
다음 장에서는 API 및 직렬 변환기에 대한 테스트 추가를 살펴볼 것입니다. 또한 flake8CI 환경에서 테스트 를 사용 하고 실행 하는 방법도 살펴 봅니다 .