django-tastypieでApiKey認証をかける
先日django-tastypieで作ったAPIにBasic認証をかける方法を試してみました。
今日は自動的に生成されるApiKeyを使うApiKey認証というのを試してみたいと思います。
Basic認証ではユーザーのパスワードが必要ですが、それが好ましくない場合はこっちを使うみたいです。
ApiKey認証をかけるにはResourceのMetaクラスにauthentication = ApiKeyAuthentication()を記述します
resources.py
from tastypie.authentication import BasicAuthentication from tastypie.resources import ModelResource from tastypie.authorization import Authorization from app.models import Book class BookResource(ModelResource): class Meta: queryset = Book.objects.all() # authentication = BasicAuthentication() authentication = ApiKeyAuthentication() authorization = Authorization()
Basic認証だとこれだけで使えたんですが、ApiKey認証の場合はもう一手間必要です。
まずUser毎にKeyを保存するためのテーブルが必要です。
tastypie/models.pyにApiKeyというモデルが定義されてます。
INSTALLED_APPSにtastypieを追加した状態でsyncdbをすればテーブルが作成されます
settings.py
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'app', 'tastypie', )
ApiKeyのレコードはUserのレコードを作成するときにsignalsを使って作成できます。
create_api_key関数がtastypie.modelsに用意されてますので、それが使えます。
models.py
# coding=utf-8 from django.db import models class Book(models.Model): title = models.CharField(max_length=255) pub_date = models.DateTimeField(auto_now_add=True) # 下記追加 from django.contrib.auth.models import User from tastypie.models import create_api_key models.signals.post_save.connect(create_api_key, sender=User)
manage.py createsuperuserでユーザーを作ってみると、ApiKeyのレコードが作成されているのがわかります
>>> from tastypie.models import ApiKey >>> ApiKey.objects.all() Out[5]: [<ApiKey: 430b1c2faed2328fd7a4133edefea90a4f06324b for user2>]
このapiキーとユーザー名をGETかPOSTのパラメータに指定してapiにアクセスできるようになります。
requestsを使ったアクセス方法はこんな感じになります
# coding=utf-8 import urllib import requests import json auth_params = { "username": u"user2", "api_key": u"430b1c2faed2328fd7a4133edefea90a4f06324b" } auth_query = urllib.urlencode(auth_params) # データを全部削除 r = requests.delete('http://localhost:8000/api/book/?' + auth_query) assert(r.status_code == 204) # データが全て消えていることを確認 r = requests.get('http://localhost:8000/api/book/?' + auth_query) assert(json.loads(r.text)["meta"]["total_count"] == 0) # 登録 payload = {'title': u'Python Cook Book', 'pub_date': "2012/9/25"} r = requests.post('http://localhost:8000/api/book/?' + auth_query, data=json.dumps(payload), headers={"Content-Type": "application/json"} ) assert(r.status_code == 201) # 取得 r = requests.get('http://localhost:8000/api/book/?' + auth_query) res = json.loads(r.text) assert(len(res["objects"]) == 1) assert(res["objects"][0]["title"] == u"Python Cook Book") assert(res["objects"][0]["id"] == "1") # idを指定して取得 r = requests.get('http://localhost:8000/api/book/1?' + auth_query) res = json.loads(r.text) assert(res["id"] == "1") assert(res["title"] == u"Python Cook Book") # 更新 payload = {'id': 1, 'title': u'Python Cook Book 2nd Edition', 'pub_date': "2013/1/25"} r = requests.post('http://localhost:8000/api/book/?' + auth_query, data=json.dumps(payload), headers={"Content-Type": "application/json"}) assert(r.status_code == 201) # 更新内容を確認 r = requests.get('http://localhost:8000/api/book/1?' + auth_query) res = json.loads(r.text) assert(res["id"] == "1") assert(res["title"] == u"Python Cook Book 2nd Edition") # idを指定して削除 r = requests.delete('http://localhost:8000/api/book/1?' + auth_query) assert(r.status_code == 204) # データが消えていることを確認 r = requests.get('http://localhost:8000/api/book/1?' + auth_query) assert(r.status_code == 404)
POSTの場合、データはjsonで渡してますが、jsonにusernameやapi_keyを設定しても401になります。
あくまでGETかPOSTでusername、api_keyという名前で渡さないといけない。
ですので、上記の例では全部クエリストリングとして渡してます。