http://www.django-rest-framework.org/
I wanted to issue a token when I first authenticated with a user ID and password, and then include that token in the request, so I implemented it as follows (use Django's User table as it is) ..
When registering user information in the User table, only the password is hashed using the Django library and registered. https://docs.djangoproject.com/en/1.8/_modules/django/contrib/auth/hashers/#make_password
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password')
write_only_fields = ('password')
read_only_fields = ('id')
def create(self, validated_data):
"""
Register after hashing password(django use default library)
"""
password = validated_data.get('password')
validated_data['password'] = make_password(password)
return User.objects.create(**validated_data)
# ......
I used the TokenAuthentication function of Django REST framework as it is. http://www.django-rest-framework.org/api-guide/authentication/
settings.py
INSTALLED_APPS = (
# .......
'rest_framework.authtoken',
)
Add a process to create a Token table
models.py
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
"""
When a new user is created, TOKEN is automatically issued.
"""
if created:
Token.objects.create(user=instance)
With the above settings, when a user is registered in the User table, a token will be issued and stored in the Token table.
Assign obtain_auth_token to any URI and create an endpoint for the client to get the token.
urls.py
from rest_framework.authtoken import views as auth_views
urlpatterns = patterns('',
url(r'^api-token-auth/', auth_views.obtain_auth_token),
)
If you POST the json file containing username and password as shown below, the token corresponding to username will be returned.
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"username":"test","password":"111111"}' https://127.0.0.1:8000/api/api-token-auth/
When making a request from the client to a URI that requires a token, it is OK if the token is packed in the Authorization of Http Header as shown below.
$ curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
If it is a normal authentication process, if you add the following settings to settings.py, it will check the contents of the token and perform the authentication process when a request comes.
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
However, if this setting is made, token authentication will be performed for all URI requests by default, so if you want to change the authentication method for some URIs, use your own Authentication as shown in the example below. You can define a class, write the authentication process in it, and specify that class in the view class.
By the way, note that the Http Header field name specified on the client is automatically prefixed with "HTTP_". It looks like Django's specifications.
authentications.py
class FooAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
#Evaluate the default fixed token during POST
if request.method == 'POST':
default_token = request.META.get('HTTP_DEFAULT_TOKEN')
try:
token = System.objects.get(key='HTTP_DEFAULT_TOKEN')
except Token.DoesNotExist:
raise exceptions.AuthenticationFailed('error')
if default_token != token.value:
raise exceptions.AuthenticationFailed('error')
return None
#If you can do something other than POST, evaluate the authentication token issued for each registered value.
else:
auth_token = request.META.get('HTTP_AUTHORIZATION')
if not auth_token:
raise exceptions.AuthenticationFailed('Authentication token is none')
try:
user = Token.objects.get(key=auth_token.replace('Token ', ''))
except Token.DoesNotExist:
raise exceptions.AuthenticationFailed('error')
return (user.user, None)
view.py
class FooViewSet(viewsets.ModelViewSet):
queryset = Foo.objects.none()
serializer_class = FooSerializer
authentication_classes = (FooAuthentication, )
# .......
On the contrary, if you want to authenticate only with some URIs, you should specify TokenAuthentication in authentication_classes of view class.
The test code used the REST framework API Client. http://www.django-rest-framework.org/api-guide/testing/ Like this.
tests.py
class UserTests(APITestCase):
def setUp(self):
"""
setUp for testing
"""
User.objects.create(username='user1', password='user1')
User.objects.create(username='user2', password='user2')
self.user1 = User.objects.get(username='user1')
self.user2 = User.objects.get(username='user2')
def test_user_list_normal1(self):
"""
user-list: normal pattern
"""
url = reverse('user-list')
expected_data = {
"count": 1,
"next": None,
"previous": None,
"results": [{
"id": 1,
"username": "user1"
}]
}
token = Token.objects.get(user=self.user1).key
#Set Token for Authorization
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)
response = self.client.get(url, None, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
#Confirm that json is returned as expected
self.assertEqual(response.data, expected_data)
Recommended Posts