django-Vue前后端分离后端流程(一)

django-Vue前后端分离后端流程

一、建立数据模型

	首先,参考了这个网页[Django+xadmin打造在线教育平台(一)](https://www.cnblogs.com/derek1184405959/p/8590360.html)https://www.cnblogs.com/derek1184405959/p/8590360.html,建立了数据模型,在users,course,study_log这三个django的子app中,每个models.py都写了相应的数据
from django.db import models
from django.contrib.auth.models import AbstractUser
import datetime
class userProfile(AbstractUser):
    id = models.AutoField('ID',primary_key=True)
    username = models.CharField('手机号',max_length=12,unique=True)
    password = models.CharField('验证码',max_length =100)
    last_send_code = models.IntegerField('最后一次发送验证码时间戳',default='0')
    sumTimes = models.IntegerField('学习总时长(s)',default=0)
class Meta:
    verbose_name = '用户个人信息'
    verbose_name_plural = verbose_name

def __str__(self):
    return self.username

可以看到,在users中,写了一个userProfile的类,id是主键,autofield意味着它可以自增,不需要手动添加,然后对于char类型的字段,都需要固定长度,整数型则不需要,这就是整个数据模型建立的过程。最好可以使用uml图建立数据模型,如[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传django-Vue前后端分离后端流程(一)_第1张图片

二、views内容编写

(1)短信接口编写

​ 最开始,学长让我使用restful方式写接口,因为我们是前后端分离的合作方式,所以restful方式可能更好接受一些,但是我不太会,就用网上的一种方式,通过restframework继承APIView来写接口,最后也是可以实现的。这里,我们需要用到阿里云短信验证码接口调用,这里以这个网页的python代码为例来写的https://www.cnblogs.com/qinyuanyuan/p/11452761.html 和https://www.cnblogs.com/hszstudypy/p/12721582.html#autoid-0-0-0,用这个代码需要有自己的阿里云账号,以及短信模板,短信代号,还要往里面充值才能使用,0.05元/条。

(2)鉴权方式

	众所周知,每个网页调用api都要鉴权,这样可以防止xss攻击,将难度提升到impossible级别,这里我找了多个网页:

1.前后端常见的几种鉴权方式https://blog.csdn.net/wang839305939/article/details/78713124

2.Django中的JWT(Json Web Token认证机制)https://blog.csdn.net/dakengbi/article/details/92717351

3.Django REST framework 的JWT Token获取用户信息https://www.jianshu.com/p/3aa4a9625717

4.Django中的JWT(Json Web Token认证机制)https://blog.csdn.net/python_nice/article/details/81474794

5.JWT认证在Django中的简单实现https://www.jianshu.com/p/9f707289478b

6.django使用jwt

7.常见的鉴权方式,你真的不想知道吗https://www.jianshu.com/p/4a00c0c3bf1d

8.Python JWT 实战 https://www.jianshu.com/p/c091a5473e35

​ 从上面可以看出,我选择的是jwt方式,为什么选择这个,根据多种鉴权方式对比,jwt符合简便易用,而且安全高效的原则。但是我的jwt实现方式是手动实现,通过调用python的jwt插件来实现,不便于使用,更加不安全,因为这样通过get方式可以get到jwt加密的payload相关,从而反向得到加密信息,所以一定要使用restframwork的jwt,这是最好的方式。这是我的代码,jwtloginView是首次登陆时调用的API,其中post方式是较为安全的递送param的方式,jwt的加密过程就在其中

class JwtLoginView(APIView):
    
    def post(self, request, *args, **kwargs):
        """
        
        @api {post} /api/firstLogin/ 首次登陆或token失效后登陆
        @apiGroup Login
        @apiName 首次登陆或token失效后登陆
        @apiVersion 0.6.0
        @apiParam {String} username 用户名(手机号)
        @apiParam {String} password 验证码(密码)
        @apiParamExample {json} 参数示例
        {
            "username":"12345678901"
            "password":"1234"
        }
        @apiError {String} message 错误信息
        @apiErrorExample {json} error-example
        {
            'message': '用户名或验证码错误'
        }
        @apiSuccess {String} token token
        @apiSuccess {String} username 用户名(手机号)
        @apiSuccess {String} last_send_code 用户上次获取验证码时间戳
        @apiSuccess {Int} sumTimes 用户总学习时长
        @apiSuccess {Int} courseTimes 课程总时长
        @apiSuccess {Int} study_id 学习卡id
        @apiSuccess {Int} last_log 最后一次学习课程学习时长
        @apiSuccess {String} last_study 最后一次学习时间戳
        @apiSuccess {Int} course_id 最后一次学习课程id
        @apiSuccess {String} course_name 最后一次学习课程标题
        @apiSuccess {String} description 最后一次学习课程描述
        @apiSuccess {Int} length 最后一次学习课程课程长度
        @apiSuccess {String} path 课程url路径
        @apiSuccess {String} image 课程图片路径
        @apiSuccessExample {json} success-example
        {
            "token":"eyJ0eXAiOiJqd3RfIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFsZXgiLCJleHAiOjE1OTQzMDM0OTh9.y5K-c7rkMQWLjlJTTZvMKIBNsSZ0s1aHrfywxR9ytkE",
            "username": "12345678901",
            "last_send_code": 20200710,
            'study_id':1,
            'last_log':15,
            'last_study':20200717,
            'course_id': 1,
            'course_name':'math',
            'description':'math',
            'length':15,
            'path':'http://example.com/123',
            'image':'/static/xxx.jpg'
        }

        """
        username = request.data.get("username")
        password = request.data.get("password")
        user_obj = userProfile.objects.filter(username=username, password=password).exists()
        if not user_obj:
            return JsonResponse({'message': '用户名或验证码错误'})
        user = userProfile.objects.get(username=username)

        import jwt
        import datetime
        salt = "fadsf$@%#%#%gsfdgsdgfd"
        headers = {
            "typ": "jwt_",
            "alg": "HS256",
        }
        payload = {
            "user_id": user.id,
            "username": username,
            "exp": datetime.datetime.utcnow() + datetime.timedelta(days=65)
        }
        
        token = jwt.encode(payload=payload, key=salt, headers=headers).decode("utf-8")
        studyLogExists = study_log.objects.filter(user = user.id)
        courseQuery =course.objects.all()
        courseTimes = 0
        user.sumTimes = 0
        user.save()
        for m in courseQuery:
            courseTimes = courseTimes + m.length
        
        if len(courseQuery) != len(studyLogExists):
            courseRange = len(courseQuery)
            for i in range(courseRange):
                studyLog = study_log.objects.get_or_create(
                last_log = 0,
                last_study = str(datetime.datetime.now().strftime("%Y%m%d%H%M%S")),
                course = i+1,
                user = user
            )
            return JsonResponse({
            'token': token, 
           'username': user.username,
           'last_send_code': user.last_send_code,
           'sumTimes':user.sumTimes,
           'courseTimes':courseTimes,
           'last_log':0,
           'course_id': None,
           'course_name':None,
           'description':None,
           'length':None,
           'path':None,
           'image':None,
           })
        else:
            studyLogAll = study_log.objects.filter(user=user.id)
            studyLog = studyLogAll.order_by('-last_study').first()
            courseGet = course.objects.get(course_id=studyLog.course)
            sumTimes = 0
            for s in studyLogAll:
                sumTimes += s.last_log
            user.sumTimes = sumTimes
            user.save()
        return JsonResponse({
            'token': token, 
           'username': user.username,
           'last_send_code': user.last_send_code,
           'sumTimes':user.sumTimes,
           'courseTimes':courseTimes,
           'study_id':studyLog.study_id,
           'last_log':studyLog.last_log,
           'last_study':studyLog.last_study,
           'course_id': courseGet.course_id,
           'course_name':courseGet.course_name,
           'description':courseGet.description,
           'length':courseGet.length,
           'path':courseGet.path,
           'image':courseGet.image
           })

​ 接下来是解密过程,在JwtAlwaysLogin中,是含解密和登陆过程的一个apiview的实现

class JwtAlwaysView(APIView):
    
    def get(self, request, *args, **kwargs):
        """
        @api {get} /api/alwaysLogin/ 使用token登陆(14天)
        @apiGroup Login
        @apiName 使用token登陆(14天)
        @apiVersion 0.6.0
        @apiParam {String} token token
        
        @apiParamExample {json} 参数示例
        {
            "token":"eyJ0eXAiOiJqd3RfIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFsZXgiLCJleHAiOjE1OTQzMDM0OTh9.y5K-c7rkMQWLjlJTTZvMKIBNsSZ0s1aHrfywxR9ytkE"
        }
        @apiError {String} msg 错误信息
        @apiErrorExample {json} error-example:
            HTTP/1.1 411 Not Found
            {
                'msg': 'token失效',
                'msg': 'token认证失败',
                "msg": "非法token"
            }
        @apiSuccess {String} username 用户名(手机号)
        @apiSuccess {String} last_send_code 用户上次获取验证码时间戳
        @apiSuccess {Int} sumTimes 用户总学习时长
        @apiSuccess {Int} courseTimes 课程总时长
        @apiSuccess {Int} study_id 学习卡id
        @apiSuccess {Int} last_log 最后一次学习课程学习时长
        @apiSuccess {String} last_study 最后一次学习时间戳
        @apiSuccess {Int} course_id 最后一次学习课程id
        @apiSuccess {String} course_name 最后一次学习课程标题
        @apiSuccess {String} description 最后一次学习课程描述
        @apiSuccess {Int} length 最后一次学习课程课程长度
        @apiSuccess {String} path 课程url路径
        @apiSuccess {String} image 课程图片路径
        @apiSuccessExample {json} success-example
        {
            "username": "12345678901",
            "last_send_code": 20200710,
            'sumTimes':124,
            'courseTimes':333,
            'study_id':1,
            'last_log':15,
            'last_study':20200720153900,
            'course_id': 1,
            'course_name':'math',
            'description':'math',
            'length':15,
            'path':'http://example.com/123',
            'image':'/static/xxx.jpg'
        }

        """
        # 获取token并验证
        token = request.GET.get("token")
        import jwt
        from jwt import exceptions
        result = None
        msg = None
        salt = "fadsf$@%#%#%gsfdgsdgfd"
        try:
            result = jwt.decode(token, salt, True)
            user_id = result['user_id']
            user = userProfile.objects.get(id = user_id )
        except exceptions.ExpiredSignatureError:
            msg = "token失效"

        except exceptions.DecodeError:
            msg = "token认证失败"
        except exceptions.InvalidTokenError:
            msg = "非法token"

        if not result:
            return JsonResponse({"msg": msg}) 
              
        studyLogAll = study_log.objects.filter(user=user.id)
        studyLog = studyLogAll.order_by('-last_study').first()
        courseGet = course.objects.get(course_id=studyLog.course)
        courseQuery =course.objects.all()
        courseTimes = 0
        sumTimes = 0
        for m in courseQuery:
            courseTimes = courseTimes + m.length
        for s in studyLogAll:
                sumTimes += s.last_log
        user.sumTimes = sumTimes
        user.save()
        return JsonResponse({
           'username': user.username,
           'last_send_code': user.last_send_code,
           'sumTimes':user.sumTimes,
           'courseTimes':courseTimes,
           'study_id':studyLog.study_id,
           'last_log':studyLog.last_log,
           'last_study':studyLog.last_study,
           'course_id': courseGet.course_id,
           'course_name':courseGet.course_name,
           'description':courseGet.description,
           'length':courseGet.length,
           'path':courseGet.path,
           'image':courseGet.image
           })

(3)接口封装相关及接口文档

​ 这里接口封装,参考Django封装交互接口https://blog.csdn.net/weixin_41827390/article/details/81286154,以及json文档相关信息https://www.runoob.com/python/python-json.html,通过(2)中的代码块,在最前面import JsonResPonse 最后return JsonResponse()就是返回的json格式信息,可以方便前端的获取及使用,一定要这样return才可以。

	至于接口文档,推荐使用apidoc,apiDoc - 超简单的文档生成器https://zhuanlan.zhihu.com/p/83487114,参考这个写出来的api文档易于读取,且书写方便,代码及图片示例在下面.
"""
        @api {get} /api/alwaysLogin/ 使用token登陆(14天)
        @apiGroup Login
        @apiName 使用token登陆(14天)
        @apiVersion 0.6.0
        @apiParam {String} token token
        
        @apiParamExample {json} 参数示例
        {
            "token":"eyJ0eXAiOiJqd3RfIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFsZXgiLCJleHAiOjE1OTQzMDM0OTh9.y5K-c7rkMQWLjlJTTZvMKIBNsSZ0s1aHrfywxR9ytkE"
        }
        @apiError {String} msg 错误信息
        @apiErrorExample {json} error-example:
            HTTP/1.1 411 Not Found
            {
                'msg': 'token失效',
                'msg': 'token认证失败',
                "msg": "非法token"
            }
        @apiSuccess {String} username 用户名(手机号)
        @apiSuccess {String} last_send_code 用户上次获取验证码时间戳
        @apiSuccess {Int} sumTimes 用户总学习时长
        @apiSuccess {Int} courseTimes 课程总时长
        @apiSuccess {Int} study_id 学习卡id
        @apiSuccess {Int} last_log 最后一次学习课程学习时长
        @apiSuccess {String} last_study 最后一次学习时间戳
        @apiSuccess {Int} course_id 最后一次学习课程id
        @apiSuccess {String} course_name 最后一次学习课程标题
        @apiSuccess {String} description 最后一次学习课程描述
        @apiSuccess {Int} length 最后一次学习课程课程长度
        @apiSuccess {String} path 课程url路径
        @apiSuccess {String} image 课程图片路径
        @apiSuccessExample {json} success-example
        {
            "username": "12345678901",
            "last_send_code": 20200710,
            'sumTimes':124,
            'courseTimes':333,
            'study_id':1,
            'last_log':15,
            'last_study':20200720153900,
            'course_id': 1,
            'course_name':'math',
            'description':'math',
            'length':15,
            'path':'http://example.com/123',
            'image':'/static/xxx.jpg'
        }

        """

django-Vue前后端分离后端流程(一)_第2张图片

(4)view对应的urls.py的匹配实现

​ 这个,具体参考代码,django的urls中的实现,通过urls.py一眼就能看出怎么弄。

from django.urls import path
from . import views
from . import serializers
urlpatterns = [
   path('api/users/', views.UsersAPIVIew.as_view(),name='全部用户操作'),
   path('api/user/', views.UserAPIView.as_view(),name='单个用户操作'),
   path('api/code/',views.CodeAPIView.as_view(),name='验证码'),
#    # 认证令牌
#    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
#    # 刷新令牌
#    path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
#    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
# #    path('api/login/', serializers.MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/firstLogin/',views.JwtLoginView.as_view(),name='首次登陆或token失效后登陆'),
    path('api/alwaysLogin/',views.JwtAlwaysView.as_view(),name='再次登录'),
    path('api/wxLogin/',views.wxLoginView.as_view(),name='微信登录')

]


你可能感兴趣的:(python,django,后端)