• 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": "u795eu821fu8def999u53f7",
                "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)
    
  • 相关阅读:
    关于C 语言的字符串常量拼接
    网络处理器简介
    杨先生的博客目录(持续更新......)
    搭建json-server服务
    Spring boot + Mybatis + SQLite 搭建blog API
    使用json-server POST 数据结果只有id
    解决python查询数据库字段为decimal类型的数据结果为科学计数法的问题
    Maven仓库安装配置及使用
    Jekins发送Allure测试报告邮件
    Jenkins发送邮件配置
  • 原文地址:https://www.cnblogs.com/jenvid/p/12952099.html
Copyright © 2020-2023  润新知