diadia

興味があることをやってみる。自分のメモを残しておきます。

django-rest-frameworkのユーザー認証を実現させる

https://www.django-rest-framework.org/api-guide/authentication/

https://blog.sakaki333.com/blog/view/78

はじめに

この記事はdjnagoをバックエンドとしてandroidアプリケーションを作成することを前提する。 androidアプリケーションのユーザー認証周りについてどのように実装していけばよいかについて書く。djangoをバックエンドにandroidのアプリを作成する記事をあまり見つけることができなかったのでこの記事が誰かの力になれば幸いである。 構成としては、djangoの認証周りの設定を記述したあとに、androidの実装について記述する。

Basic認証のサンプル

from rest_framework.views import APIView
from items.models import Item
from .serializers import ItemSerializer
from .serializers import ItemSerializer
from rest_framework.authentication import BasicAuthentication
from rest_framework import permissions



class TestAPIView(APIView):

    authentication_classes = (BasicAuthentication,)
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, *args, **kwargs):
        

        item_objects = Item.objects.all()
        serializer = ItemSerializer(item_objects, many=True)
        return Response(serializer.data)

TestApiViewにrequestを送ると、データが表示されずにログインフォームが表示される。

これはauthentication_classes = (BasicAuthentication,)を設定することでログインフォームが表示されたのである。

ちなみにauthentication_classes = (TokenAuthentication,)とすればTokenによる認証に変更することもできる。これについては後述する。

androidアプリケーションで使用する場合には、毎回フォームに認証情報をPOSTする使い方をすればコンテンツを表示できると思われる。

https://grandbig.github.io/blog/2016/02/07/android-login-activity/

時間がある時にこの続きを書く。

別の方法でユーザー認証を実現させる

django-rest-authを利用して認証周りを実装する

まずユーザー周りのエンドポイントを実装する

https://django-rest-auth.readthedocs.io/en/latest/index.html

上記はdjango-rest-framwork用のユーザー認証等を実現させるためにあるライブラリである。django-allauthのようにpipでインストールするとエンドポイントが自動的に決定される。このdjango-rest-authで決められたエンドポイントにアクセスすることで、ログイン認証ができたり、ユーザー登録など実装の手間を省くことができる。

django-rest-authでエンドポイントを設定したら後はアンドロイドアプリで指定のエンドポイントを記述すれば認証周りの仕組みを実装できそうだ。 具体的な例として、ユーザー登録の実装方法を考える。

f:id:torajirousan:20200125172645p:plain

http://localhost:8000/rest-auth/registration/にアクセスすると上記の画面を表示する事ができる。(django-rest-authをインストールすることが前提である。) 画像下部のUsername, Email, Password1, Password2フォームを埋めてPOSTボタンを押すとトークンが返ってくる。

f:id:torajirousan:20200125173600p:plain

送信内容に誤りがなければ、トークンが返されることを上記の画像で確認できる。このトークンを使って次回からログイン認証を行うことになる。

ちなみにdjango-rest-frameworkにはベーシック認証とトークンを使った認証が準備されており、この例ではトークンを使った認証を採用している。

参考:https://www.django-rest-framework.org/api-guide/authentication/#api-reference

このトークン認証の設定にはINSTALLED_APPSに'rest_framework.authtoken'を追加することと、settings.pyにTokenAuthenticationを追加すれば良い。

これらを確認できたらユーザー認証周りのエンドポイントは準備できたことになる。

エンドポイントにブラウザではない方法でHTTPで接続してみる

先程作成したユーザー登録するエンドポイント/rest-auth/registration/にはKotlinでアクセスすることになる。だからdjango-rest-frameworkのユーザーインターフェースを使わず、純粋なHTTPプロトコルでユーザー登録ができるか確認するべきである。その手段としてcurlを用いる。

curlについては必要最低限ここにまとめた。

curl -X POST -d 'username=&email=testmyga@gmail.com&password1=1234tweet&password2=1234tweet' http://127.0.0.1:8000/rest-auth/registration/ 

上記のように入力すると、ターミナルには{"key":"b2fa2985300735de965fe2ec80cb0247e2f7bccf"}のような形で返される。このように返されたらHTTPプロトコルでユーザー登録を実行することができた証拠なので、この形式をKotlinのアクティビティ(.ktファイル)で記述すれば良いと判断できる。なお、curlのコマンドの例ではusernameを入力不要の設定にしてる。

具体的に実装するには

ログインのエンドポイントまたはユーザー登録のエンドポイントでTokenを取得する。

//あるクラスのメソッド
    fun logInByBasicAuth() {

        val retrofit = Retrofit.Builder()
            .baseUrl("http://10.0.2.2:8000/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        val service = retrofit.create(APIService::class.java)
        val response = service.login(email, password).enqueue(CustomCallBack())
    }



class CustomCallBack : Callback<LoginResultModel> {
    //BasicAuthにおいて認証データが適切な場合にはステータス:200が返される。
    //逆に不適切な場合にはステータス:400が返される。
    //したがって認証情報が不適切な場合にはnon_field_errorsが返されることは全くない

    override fun onResponse(call: Call<LoginResultModel>, response: Response<LoginResultModel>) {
        print("レスポンスを回収")
        println(response.code())
        if (response.code() == 200) {
            //println(response.body()?.key)
            authToken = response.body()?.key


        }else if(response.code() == 400) {
            println(response.message())
            }
    }
    override fun onFailure(call: Call<LoginResultModel>, t: Throwable) {
        //TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        println("エラーハンドリング")
    }
}

//
interface APIService {
    @FormUrlEncoded
    @POST("rest-auth/login/")
    fun login(@Field("email") email: String?, @Field("password") password: String?) : Call<LoginResultModel>
}

このトークンを何らかの形で保存する。自分の場合はAccountManagerクラスのsetAuthTokenメソッドでTokenを保存する。

次回からこのTokenをhttpプロトコルのヘッダーに添付して通信を行えば実装できる。