djangoのログイン機能について　2021/2/18

#1.認証（Authentication） と 承認（Authorization） の違いについて
###認証 ( Authentication )
サービス へ アクセス してきた ユーザー が `「本人であるかどうか」` を検証する。

![authentication.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/4d1c51d7-6d47-15e4-41db-e5676c032397.png)


###承認 ( Authorization )
サービス へ アクセス してきた 認証済みユーザー が `「指定した リソース へ アクセスできるかどうか」` を制御する。

![authorization.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/b851872a-b133-d441-ee01-b9fc57cc53ca.png)


-------------------------
#djangoのaccountを認証済みでないと実行できないapiにする（authorization）


```python:viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [permissions.AllowAny]
```
permission_classes というフィールドが登場しましたが、Django REST framework ではここに Permission クラスを設定することでアクセス権を設定するようにする。（ここでは誰でもアクセスできる）
↓

```python:viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [permissions.IsAuthenticated]
```
permission_classes に IsAuthenticated を指定した。これで認証済みでないと実行できないAPIにすることができた。

すると、、、

![スクリーンショット 2021-02-18 11.36.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/2994d1b9-feaa-ec00-9c79-71a596c30db8.png)

これで無事にセキュリティーレベルをあげることができました！

ここではアクセスするためにはtokenを登録する必要があるがそのAuthenticateの設定方法は`setting.py`で管理できる。

```python:setting.py
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication'
    ]
}
```

ここではトークンでアクセスできるようにしたいので`'rest_framework.authentication.TokenAuthentication'`を追加した。


-----------------------
#トークンの作成・取得方法
1.ユーザーを取得する

```:ターミナル
account = Account.objects.filter(id=14).first()
<output> < Account: 14>

account.user
<output> <User: 'ユーザー名'>

```


2.Tokenを作成・取得する

トークンの作り方

```:ターミナル
from rest_framework.authtoken.models import Token

token = Token.objects.create(user=...)
 #取得の時はcreate→getに変える
print(token.key)
```
詳しくはdjango rest frameworkのドキュメントを参照。
https://www.django-rest-framework.org/api-guide/authentication/

---------------------------
#get_queryset関数
AuthTokenでセキュリティーをあげたが、このままだとトークンを取得して一度ログインしてしまうとどのAccountの情報も取得できてしまうのでget_queryset関数をオーバーライドする

>get_querysetとは？
>>get_querysetメソッドは主にListViewで使われますが、モデルインスタンスの一覧を返すメソッドです。例えばfilterメソッドで絞り込んだり、柔軟に一覧を取得したい場合はget_querysetメソッドを上書きしてそのように書きます。

```python:viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    # permission_classes = [permissions.IsAuthenticated]
    
    def get_queryset(self):
        queryset = self.queryset.filter(user=self.request.user)
        return queryset  
```

クエリセットを取得するget_querysetをオーバーライドする

-----------------------
#to_representation関数

###やりたいこと
Accountモデルのフィールドの中にトークンを追加したい。

```python:serializers
 def to_representation(self, value)
    output = super().to_representation(value)
    print(output) 
      #ターミナル上で確認する
    return output
　　　　#ポストマンで確認する
```
↓

outputの辞書型にtokenというキーを追加する

上のコードで示すvalueはaccountのオブジェクトの中身と同じなのでその中にトークンのkeyとvalueを追加する
まず最初に、valueオブジェクトの型を確認するために
`print(type(value))`を使う

>type()とはオブジェクトの型を取得、確認
>>print(type('string'))
# <class 'str'>


```python:serializars.py
    def to_representation(self, account):
        print("hogehoge", type(account))
        output = super().to_representation(account)
        token = Token.objects.filter(user=account.user).first()
        if not token:
            token = Token.objects.create(user=account.user)
        output['token'] = token.key
        print(output)
        return output
```

------------------

#Django REST Framework:1つのAPIViewの中でリクエストメソッド毎にpermission_classesを分ける

#やりたいこと
POSTの時にIsAuthenticationを外したい。なぜならアカウントがまだない時はトークンを作ることができないから

```python:viewsets.py
    def get_permissions(self):
        if self.request.method == 'POST':
            return []
        else:
            return [permissions.IsAuthenticated()]
```

こうすることで、postメソッドの時はパーミッションが必要なく、getメソッドなどの時はIsAuthenticatedをパーミッションとして採用することができる。