09.drf认证

一.token 认证

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

  • 1.1 settings 配置
# install
    'rest_framework.authtoken',
    
# 
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ),
  • 1.2 migrate
python manage.py migrate
  • 1.3 配置 url
from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]


# drf 文档原本内容如下
from rest_framework.authtoken import views
urlpatterns += [
    url(r'^api-token-auth/', views.obtain_auth_token)
]
  • 1.4 curl 获取token
curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456##"}' http://127.0.0.1:8000/api-token-auth/
{"token":"4dd5411643aeb990ffab58118695cbdb98b253b6"}
全局配置需要认证才能访问
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.DjangoModelPermissions',
        'utils.permissions.Permissions',
        # 'rest_framework.permissions.AllowAny',
    )

  • 1.5 携带token访问
curl -X GET http://127.0.0.1:8000/idcs/ -H 'Authorization: Token 4dd5411643aeb990ffab58118695cbdb98b253b6'|python -m json.tool
  • 1.6 返回
{
  "total": 104,
  "next": "http://127.0.0.1:8000/idcs/?pagerCount=2",
  "previous": null,
  "data": [{
    "id": 1,
    "name": "亚太机房",
###########
{
  "detail": "Authentication credentials were not provided."
}
# 没有配置 token 认证类无法使用 token 登录
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ),
  • 1.7 视图中配置
    authentication_classes = (TokenAuthentication,)
    permission_classes = (AllowAny,)

二. drf JWT 认证

https://github.com/jpadilla/django-rest-framework-jwt
https://jpadilla.github.io/django-rest-framework-jwt/

  • 2.1 安装
pip install djangorestframework-jwt
  • 2.2 settings
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ),
  • 2.3 url
from rest_framework_jwt.views import obtain_jwt_token
#...

urlpatterns = [
    '',
    # ...

    url(r'^api-token-auth/', obtain_jwt_token),
]
  • 2.4 获取 token`
# 
curl -X POST -d "username=admin&password=123456##" http://localhost:8000/api-token-auth/

# json格式
curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456##"}' http://localhost:8000/api-token-auth/
  • 2.5 返回 token
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo"}
  • 2.6 携带 token 访问
curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo" http://localhost:8000/idcs/

  • 2.7返回
# 错误 token 返回:
{"detail":"Error decoding signature."}
# 正确 token 返回
jenvid@jenvidVM:~/01web/ops/vueAdmin-template$ curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo" http://localhost:8000/idcs/ |python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1324  100  1324    0     0   107k      0 --:--:-- --:--:-- --:--:--  107k
{
    "data": [
        {
            "address": "\u795e\u821f\u8def999\u53f7",
            "email": "xxx@com.cn",

三. 修改过期时间,默认是 300s

  • 3.1 settings
import datetime
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
    }
  • 3.2 过期返回
{
    "detail": "Signature has expired."
}

四.自定义认证,用户 手机号认证

AUTHENTICATION_BACKENDS = (
    'users.views.CustomBackend',
)

import datetime
#有效期限
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),    #也可以设置seconds=20
    'JWT_AUTH_HEADER_PREFIX': 'JWT',                       #JWT跟前端保持一致,比如“token”这里设置成JWT
}

# users.views.py

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q

User = get_user_model()

class CustomBackend(ModelBackend):
    """
    自定义用户验证
    """
    def authenticate(self, username=None, password=None, **kwargs):
        try:
            #用户名和手机都能登录
            user = User.objects.get(
                Q(username=username) | Q(mobile=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None

五. python jwt

https://github.com/Jenvid/jwtdemo

  • 5.1 自定义认证类 /user/extensions/auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed
from jwt import exceptions
import jwt

from django.conf import settings


class JwtQueryParamsAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 获取token
        # 1.切割
        # 2.解密第二段,判断是否过期
        # 3.验证第三段合法性
        token = request.query_params.get('token') #
        SALT = settings.SECRET_KEY
        try:
            payload = jwt.decode(token, SALT, True)  # True 使用集成的时间等字段校验
        except exceptions.ExpiredSignatureError:
            msg = 'token已失效'
            raise AuthenticationFailed({'code':400,'error':msg})
        except jwt.DecodeError:
            msg = 'token认证失败'
            raise AuthenticationFailed({'code': 400, 'error': msg})
        except jwt.InvalidTokenError:
            msg = '非法的token'
            raise AuthenticationFailed({'code': 400, 'error': msg})
        # 3种返回值
        # 1.抛出异常后,后面的代码都不会执行了
        # 2.return 元组(1,2) 认证通过, 在views中调用 request.user = 第一个元素, request.auth = 第二个值
        # 3.None, 等待下一次认证,不用管
        # if not payload:
        #     return Response({'code': 2001, 'error': msg})
        print('payload: ', payload)
        return (payload,token) # 1=request.user
  • 5.2 视图函数生成 token
class JwtLoginView(APIView):
    def post(self,request,*args,**kwargs):
        user = request.data.get('username')
        pwd = request.data.get('password')
        user_object = UserInfo.objects.filter(username=user,password=pwd).first()

        if not user_object:
            return Response({'code':1000,'error':'用户名密码错误'})
        # 通过jwt生成token

        # 构造header,不写表示使用默认
        headers = {
            'typ': 'JWT',
            'alg': 'HS256'
        }
        # 构造payload
        payload = {
            'user_id': user_object.id,  # 自定义用户ID
            'username': user_object.username,  # 自定义用户名
            'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1)  # 超时时间
        }
        token = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
        return Response({'code':1001,'data':token})
  • 5.3 视图post中使用 jwt 认证
from user.extensions.auth import JwtQueryParamsAuthentication
from user.utils.jwt_auth import create_token
class ProLoginView(APIView):
    authentication_classes = [] # 为空表示不应用认证
    def post(self,request,*args,**kwargs):
        user = request.data.get('username')
        pwd = request.data.get('password')
        user_object = UserInfo.objects.filter(username=user,password=pwd).first()

        if not user_object:
            return Response({'code':1000,'error':'用户名密码错误'})
        payload = {
            "id":user_object.id,
            "username":user_object.username
        }
        token = create_token(payload)
        return Response({'code':1001,'data':token})
  • 5.4 视图get使用jwt认证
class ProOrderView(APIView):
    # authentication_classes = [JwtQueryParamsAuthentication,] # 加入 settings 全局配置
    def get(self, request, *args, **kwargs):
        print(request.user)  #  拿到payload的所有信息
        return Response('订单列表')
  • 5.5 settings 全局配置自定义 jwt 认证
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES" : ['user.extensions.auth.JwtQueryParamsAuthentication']
}

六.drf token base64解密 增加返回token

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from utils.base_password import decode_password
import base64
def decode_password(en_password):
    byte_password = base64.b64decode(en_password)
    password = str(byte_password, encoding='utf8')
    return password

class CustomAuthToken(ObtainAuthToken):

    def post(self, request, *args, **kwargs):
        en_password = request.data['password']
        print(en_password,type(en_password))
        password = decode_password(en_password)
        print('password:',password)
        request.data['password'] = password
        serializer = self.serializer_class(data=request.data,
                                           context={'request': request})
        print('drf接收登录数据', serializer)
        print(request.data)

        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)

        data = {
            'token': token.key,
        }
        return Response(data)
posted @ 2020-05-25 18:31  Jenvid  阅读(705)  评论(0编辑  收藏  举报