【Django+Vue3 线上教育平台项目实战】登录功能模块之短信登录与钉钉三方登录

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 在当今的数字化时代,用户认证是任何在线服务安全性的基石。本文将简明扼要地介绍登录注册流程中的核心概念:HTTP无状态性、Session、Token与JWT,并详细阐述两种实用登录方式—— 手机号登录验证(借助容联云/云通讯服务) 与钉钉第三方登录。我们将探讨这些概念的基本原理,并深入解析两种登录方式的实现流程,旨在帮助开发者提升用户认证的安全性与便捷性。

@TOC


前言

    在当今的数字化时代,用户认证是任何在线服务安全性的基石。本文将简明扼要地介绍登录注册流程中的核心概念:HTTP无状态性、Session、Token与JWT,并详细阐述两种实用登录方式—— 手机号登录验证(借助容联云/云通讯服务)钉钉第三方登录。我们将探讨这些概念的基本原理,并深入解析两种登录方式的实现流程,旨在帮助开发者提升用户认证的安全性与便捷性。


一、几个关键概念

    首先,我们来分析登录注册流程中涉及的几个关键概念及其工作机制,特别是关于HTTP无状态性、Session机制、Token认证,以及它们如何影响 单点登录(SSO) 的实现。

1.HTTP无状态性

    HTTP协议本身是无状态的,这意味着服务器不会保留来自先前请求的客户端信息。每次请求都被视为完全独立的,服务器不会记住之前发生了什么。正是由于HTTP的无状态性,服务器无法直接记住用户是否已经登录或注册。这意味着每次用户请求都需要重新验证用户的身份,这在没有额外机制的情况下是不可行的。这种设计虽然简化了服务器的实现,但也要求开发者实现一些机制来跟踪用户会话和状态。

2.Session机制

工作原理:

  • 1. 请求发起:客户端(如浏览器)向服务器发起请求。
  • 2. 验证与生成Session:服务器验证请求(如登录验证),验证通过后,在服务器上创建一个Session对象,并为其分配一个唯一的标识符(如JSESSIONID)。这个Session对象可以存储用户的信息,如用户名、权限等。
  • 3. 返回Session ID:服务器将JSESSIONID作为响应的一部分(通常通过Set-Cookie头部)发送给客户端。
  • 4. 客户端存储:客户端(浏览器)将JSESSIONID存储在Cookie中。
  • 5. 后续请求:在后续的请求中,客户端会自动将JSESSIONID包含在请求头(如Cookie)中发送给服务器。
  • 6. 验证Session:服务器通过JSESSIONID在服务器上查找对应的Session对象,如果找到,则继续处理请求;如果未找到,则可能表示用户未登录或Session已过期。

不适合单点登录的原因:

  • 每个应用或服务都可能有自己的Session管理机制,这使得跨多个应用或服务共享登录状态变得复杂。

3.Token认证

工作原理:

  • 1.请求发起:客户端向服务器发起请求(如登录请求)。
  • 2.验证与生成Token:服务器验证请求(如用户名和密码),验证通过后,生成一个唯一的Token(通常是一个加密的字符串),并将其存储在数据库中(或缓存中)。
  • 3.返回Token:服务器将Token作为响应的一部分发送给客户端。
  • 4.客户端存储:客户端将Token存储在Cookie、LocalStorage或SessionStorage中(取决于具体实现和安全需求)。
  • 5.后续请求:在后续的请求中,客户端将Token包含在请求头(如Authorization: Bearer )中发送给服务器。
  • 6.验证Token:服务器通过查询数据库(或缓存)来验证Token的有效性,如果Token有效,则继续处理请求。


适合单点登录的原因:

  • Token可以在多个应用或服务之间共享,只要它们能够访问存储Token的数据库或缓存。
  • 通过适当的认证服务器(如OAuth2.0中的授权服务器),可以实现跨域的单点登录。

    4.JWT

        JWT(JSON Web Token) 是一种无状态的Token认证机制,它允许服务器在Token中直接包含用户的身份信息和其他必要的验证信息。客户端在每次请求时携带这个Token,服务器通过验证Token来识别用户的身份和权限。

JWT的组成部分:
JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),每一部分都通过Base64编码后进行传输。

  • 头部(Header):包含了令牌的元数据,如令牌的类型(JWT)和所使用的签名算法(如HMAC SHA256或RSA)。
  • 载荷(Payload):包含了实际要传输的用户信息和其他元数据。这些信息可以是用户的姓名、角色、权限、到期时间等。载荷是JWT的主体部分,用于传递用户信息。
  • 签名(Signature):用于验证JWT的完整性和真实性。签名通过对头部和载荷进行哈希运算,并使用私钥(在生成JWT时)或公钥(在验证JWT时)进行加密生成。接收者可以使用公钥对签名进行解密,从而验证JWT的真实性和来源。

  • 加密: base64(头部).base64(载荷).HS256(base64(头部).base64(载和),'盐')

  • 解密: HS256(base64(头部).base64(载荷),'盐') 生成一个新的签名和传递过来的签名进行对比

根据上述内容,我们可以构建一个简单的jwt工具类用于jwt的加密解密:

from fuguang_back.settings import SECRET_KEY
import jwt
import time

class MyJwt():
    def __init__(self):
        self.secret = SECRET_KEY

    # 加密
    def jwt_encode(self, payload):
        # 载荷、盐、加密方式
        return jwt.encode(payload, self.secret, algorithm='HS256')

    # 解密
    def jwt_decode(self, token):
        # token,盐、解密方式
        return jwt.decode(token, self.secret, algorithms=['HS256'])


mjwt = MyJwt()
# user = {"id":1,"name":"zhangsan","exp":int(time.time()) + 3600}
# token = mjwt.jwt_encode(user)
# print(token)
# token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6InpoYW5nc2FuIiwiZXhwIjoxNzE5OTI0OTQ5fQ.rTPpHAx-92Pz1CmoRdO-yfGlAhAfuMInju2bFi1iixs"
# data = mjwt.jwt_decode(token)
# print(data)
# {'id': 1, 'name': 'zhangsan', 'exp': 1719924949}

二、通过手机号验证码登录

手机号登录流程:

  • 1.在登录界面输入手机号,点击发送验证码
  • 2.写一个发送验证码的接口,获取手机号(正则有效性验证),限制一分钟内只能发一次,查询redis中是否存在,如果存在返回已经发过,不存在调用发送,发送成功后存入redis
    • 3.用户输入验证码点击登录
    • 4.写一个登录接口
      • 获取用户输入的手机号和验证码
      • 通过手机号查询redis获取验证码,如果存在,则对比验证码
      • 通过手机号查询用户表,如果存在获取用户信息生成 jwt token,如果不存在,写入用户表,用户信息生成 jwt token返回给客户端
  • 5.客户端把token存在 localStorage中,以后每次请求在头部携带token。vue设置 interceptors拦截器,对每次请求前统一在头部加token
  • 6.服户端验证采用中间件方式实现。自定义中间件,继承MiddiwareMinxin类重写process_request.方法中
    • 定义白名单,在登录前需要操作的接口放到白名单中
      • 如果不在白名单,获取token,验证
      • 验证是否被修改,是否过期,是否已经退出 (点击退出,把token存入redis,加一个过期时间),任何一个问题,return 401没有权限操作,通过继续下一步操作
  • 安全问题及优化
    • 1.token加过期时间 retoken。
      • 登录成功后返回 token(3小时) retoken(4小时),当快到期的时候客户端携带retoken来换取新的token更新
    • 2.oa系统公司内部使用加ip地址过滤
    • 3.https证书加密

1.前端短信登录界面

在这里插入图片描述

src\components\Login.vue

  <!-- 1-短信登录 -->
  <div class="inp" v-show="user.login_type==1">
    <input v-model="user.account" type="text" placeholder="手机号码" class="user">
    <input v-model="user.code"  type="text" class="code" placeholder="短信验证码">
    <el-button id="get_code" type="primary" @click="sendsms">获取验证码</el-button>
    <div class="rember">
      <label>
        <input type="checkbox" class="no" v-model="user.rememberMe"/>
        <span>记住我</span>
      </label>
      <p>忘记密码</p>
    </div>
    <button class="login_btn" @click="loginmobile">登录</button>
    <p class="go_login" >没有账号 <router-link to="/register">立即注册</router-link></p>
  </div>
// 点击事件:发送短信验证码
const sendsms =()=>{
   
   
  //在前端正则验证手机号是否正确
  let reg= /^1[3-9]\d{9}$/
  if (!reg.test(user.account)){
   
   
    alert("[前端]手机号校验失败!")
    return false;
  }
  http.get(`/sendsms/?phone=${
     
     this.account}`).then((result) => {
   
   
    if (result.data.code == "200") {
   
   
      alert("发送成功!")
    } else {
   
   
      alert(result.data.message)
    }
  }).catch((err) => {
   
   
    alert(err)
  });
}
// 点击事件:登录按钮
const loginmobile =()=>{
   
   
  http.post("/login/",{
   
   "mobile":user.account,"code":user.code}).then(res => {
   
   
    if (res.data.code=="200") {
   
    //登录成功
      localStorage.setItem('userid',res.data.userid); //客户端存储userid
      localStorage.setItem('token',res.data.token); //客户端存储token
      return router.push("/"); //跳转主页
    } else {
   
   
      return router.push("/login"); //登录失败-->调整登录页面
    }
  }).catch((err) => {
   
   
    console.log(err);
  });
}

2.发送短信接口与短信登录接口

  这里使用容联云服务,来通过短信发送验证码

云通讯后台管理:https://console.yuntongxun.com/member/numbermanager
容联云短信开发手册:https://doc.yuntongxun.com/pe/5f029ae7a80948a1006e776e

参考容联云短信开发手册,构建发送短信的工具类,代码如下:

#tools/common.py
from ronglian_sms_sdk import SmsSDK
import json
accId = '容联云通讯分配的主账号ID'
accToken = '容联云通讯分配的主账号TOKEN'
appId = '容联云通讯分配的应用ID'

# 发送短信
def send_message(mobile,sms_code):
    sdk = SmsSDK(accId, accToken, appId)
    tid = '1'
    mobile = mobile
    datas = (sms_code, )
    resp = sdk.sendMessage(tid, mobile, datas)
    data = json.loads(resp) #json-->对象
    print(data)
    if data['statusCode'] == '000000':
        return True
    return False

接口:发送短信(验证码)接口、登录接口
```python

user/views.py

发送验证码/获取验证码接口

class SendMobileCodeView(APIView):
def get(self, request):

    # send_sms前端: /sendsms/?phone=${this.account}/
    phone = request.GET.get('phone')
    print(phone)
    #if not re.match(r"1[3-9]\d{9}$",phone):
    #    return Response({"message":"手机号验证失败!","code":"410"})
    flag = r.get_str("phone")
    if flag:
        return Response({"message":"一分钟只能发送一次验证码,请稍稍后发送!","code":"200"})
    sms_code = random.randint(1000,9999)
    r.delete_str("sms_code")
    r.setex_str("sms_code",60*5,sms_code) # times:验证码5分钟有效期
    send_message(phone, sms_code) # 发送短信(验证码)
    r.setex_str("phone",60,phone) #同一个手机号60s内只能发送一次验证码
    return Response({"message":"发送成功","code":"200"})

手机短信登录接口

class LoginView(APIView):
def post(self, request):
myphone = request.data.get("mobile")
mycode = request.data.get("code")
sms_code = r.get_str("sms_code") #从redis中取出系统生成的验证码

    if mycode == sms_code: #与前端发送来的验证码对比
        user = UsersModel.objects.filter(phone=myphone).first()
        # 获取用户信息 生成 jwt token
        token = mjwt.jwt_encode({"userid":user.id,"exp":int(time.time()) + 3600}) 

        return Response({"code":200,"token":token,"userid":user.id})
    else:
        return Response({"code":4001,"message":"验证码错误!"})

## 3.Vue 设置interceptors拦截器
> 客户端把token存在 localStorage中,以后每次请求在头部携带token。`vue interceptors拦截器`,对每次请求前统一在头部添加token

> ***<font size=3>src\http\index.js***
```js
import axios from "axios"
import settings from "../settings";
import router from "../router"

const http = axios.create({
    // timeout: 2500,                          // 请求超时,有大文件上传需要关闭这个配置
    baseURL: settings.host,     // 设置api服务端的默认地址[如果基于服务端实现的跨域,这里可以填写api服务端的地址,如果基于nodejs客户端测试服务器实现的跨域,则这里不能填写api服务端地址]
    withCredentials: false,                    // 是否允许客户端ajax请求时携带cookie
})

// 请求拦截器 +token
http.interceptors.request.use((config) => {
    console.log("http请求之前");
    //获取登录后localStorage存储的token
    let token = localStorage.getItem('token') 
    if (token) {
        config.headers.Authorization = token;
    }
    return config;
}, (error) => {
    console.log("http请求错误");
    return Promise.reject(error);
});


// 响应拦截器
http.interceptors.response.use((response) => {
    return response;
}, (error) => {
    if (error.code === "ERR_NETWORK") {
        ElMessage.error("网络异常,无法请求服务端信息!");
    }
    if (error.response.status === 401) {
        ElMessage.error("未登录或登录超时!限制本次请求操作!请求登录后继续!");
        return router.push("/login");
    }

    return Promise.reject(error);
});

export default http;

4. 服务端验证采用自定义中间件方式实现

自定义中间件,继承MiddiwareMinxin类重写process_request.方法中

  • ​定义白名单,在登录前需要操作的接口放到白名单中
    • a.如果不在白名单,获取token,验证
    • ​b.验证是否被修改,是否过期,是否已经退出 (点击退出,把token存入redis,加一个过期时间),任何一个问题,return 401没有权限操作,通过继续下一步操作
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from tools.myjwt import mjwt
from tools.myredis import r
import time


class PermitionMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 1.定义白名单,在登录前需要操作的接口放到白名单中
        wlist = ['/register/', '/login/', '/sendsms/']
        # 获取当前的url
        path = request.path
        # 2.如果不在白名单,获取token,验证
        if path not in wlist:
            try:
                token = request.headers.get('Authorization')
                data = mjwt.jwt_decode(token)
            except:
                return JsonResponse({
   
   "code": 401, "mes": "token不存在或者被修改"})
            # data没问题 ↓
            exp = int(data['exp'])
            now = int(time.time())
            # 判断是否过期
            if now > exp:
                return JsonResponse({
   
   "code": 401, "mes": "token已经过期不能操作"})
            #是否退出,退出时存token
            value = r.get_str(token)
            if value:
                return JsonResponse({
   
   "code": 401, "mes": "用户已经退出,不能操作"})
        # ↑↑↑3.验证是否被修改,是否过期,是否已经退出 (点击退出,把token存入redis, 加一个过期时间),任何一个问题,return 401没有权限操作,通过继续下一步操作

不要忘记在主项目下的settings.py文件下配置写好的自定义中间件

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 'user.middleware.PermitionMiddleware', #自定义中间件
]

5. 操作流程及效果图如下:

1.输入手机号,点击发送验证码
在这里插入图片描述

2.手机接收验证码
在这里插入图片描述

3.输入验证码后,点击登录
在这里插入图片描述


三、通过第三方平台进行登录

三方登录,这里以钉钉为例

1.准备工作

钉钉开放平台:https://open.dingtalk.com/

    1. 进入钉钉开放平台,注册登录钉钉账号
    1. 注册应用,配置回调地址

具体操作流程如下:

1.登录开发者后台
在这里插入图片描述

2.创建应用
在这里插入图片描述

3.安全设置
在这里插入图片描述

4.凭证与基础信息
在这里插入图片描述

5.权限管理

在这里插入图片描述

2.前端钉钉登录界面

  <!-- 0-密码登录 -->
  <div class="inp" v-if="user.login_type==0">
    <input v-model="user.account" type="text" placeholder="用户名 / 手机号码 / 邮箱" class="user">
    <input v-model="user.password" type="password" class="pwd" placeholder="密码">
    <div class="rember">
      <label>
        <input type="checkbox" class="no" v-model="user.rememberMe"/>
        <span>记住我</span>
      </label>
      <p>忘记密码</p>
    </div>
    <button class="login_btn" @click="show_captcha">登录</button>
    <p class="go_login" >没有账号 <router-link to="/register">立即注册</router-link></p>
    <p><a :href="ddurl">钉钉登录</a></p>
  </div>
onMounted(()=>{
   
   
  http.get("dingtalkLogin/").then((result) => {
   
   
    ddurl.value = result.data.url;
    console.log(result.data);
  }).catch((err) => {
   
   
    alert(err)
  });
})

3.后端逻辑处理

三方登录表:

  • 字段:id、userid(外键)、方式(wb、qq、dd)、uid、token、retoken

处理流程:

  • 1.点击钉钉登录,调用钉钉三方登录接口DingTalkLogin,跳转到钉钉授权页面
  • 2.授权通过,登录成功,根据回调地址进行成功接口的回调
    • 获取code、调用授权接口、获取uid
    • 根据token获取手机号
    • 查询三方登录表
      • 不存在,在用户表中进行注册,同时更新三方登录表
        • 根据uid获取用户信息,生成token,跳转到前端页面

```python

钉钉三方登录接口

class DingTalkLogin(APIView):
def get(self, request):
from urllib.parse import quote
params = [
f"redirect_uri={quote('http://127.0.0.1:8000/user/dingtalkCallback/')}",
"response_type=code",
"client_id=dingrcnkswwakld0y5jx",
"scope=openid",
"prompt=consent"
]
url = "https://login.dingtalk.com/oauth2/auth?" + ("&".join(params))
return Response({"url": url})

钉钉回调接口(钉钉登录接口 点击登录后调用)

class DingTalkCallback(APIView):
def get(self, request):
authCode = request.query_params.get('authCode')

    # 根据authCode获取用户accessToken
    data = {
        "clientId": "钉钉开放平台-Client ID",
        "clientSecret": "钉钉开放平台-Client Secret",
        "code": authCode,
        "grantType": "authorization_code"
    }
    resp = requests.post('https://api.dingtalk.com/v1.0/oauth2/userAccessToken', json=data).json()
    accessToken = resp.get('accessToken')

    # 根据accessToken获取用户信息
    headers = {"x-acs-dingtalk-access-token": accessToken}
    resp = requests.get('https://api.dingtalk.com/v1.0/contact/users/me', headers=headers).json()
    name = resp.get('nick')
    uid = resp.get('openId')
    phone = resp.get('mobile')

    print(name)
    print(uid)
    print(phone)
    # ---
    # 登录,查询三方登录表,是否存在
    Sfuser = SfLoginModel.objects.filter(uid__exact=uid).first()
    print("三方登录表中--->")
    print(Sfuser)
    if Sfuser is None:
        # 注册用户表和三方登录表(先判断用户表是否已有用户信息)
        user = UsersModel.objects.filter(phone__exact=phone).first()
        print(user)
        if user is None:
            userinfo = {"phone":phone,"username":name, "password":"123"}
            userSer = UsersSerializer(data=userinfo)
            if userSer.is_valid():
                userSer.save()
            else:
                return Response({"code":10001,"message":userSer.errors})
        # 存在用户,写入三方登录表
        sfinfo = {"type":1,"uid":uid,"token":accessToken,"user":user.id}
        sfSer = SfLoginSerializer(data=sfinfo)
        if sfSer.is_valid():
            sfSer.save()
        else:
            return Response({"code":10001,"message":sfSer.errors})
    else:
        #三方登录表中存有信息,uid->获取用户信息,生成token
        # 根据uid获取用户信息,生成token,跳转到前端页面
        user = Sfuser.user
        Sfuser.token = accessToken
        Sfuser.save()

    # 生成jwt token 并返回前端
    payload = {"userid":user.id, "username":user.username,"exp":int(time.time()) + 3600}
    token = mjwt.jwt_encode(payload)
    payload["exp"] = int(time.time()) + 3600
    retoken = mjwt.jwt_encode(payload)
    query = [f"userid={payload['userid']}",f"username={payload['username']}",f"token={token}",f"retoken={retoken}"]


    return HttpResponseRedirect('http://localhost:3000/?' + '&'.join(query))

```

4.操作流程及效果图如下:

1.登录界面点击钉钉登录:
在这里插入图片描述

2.跳转钉钉授权登录页面
在这里插入图片描述
在这里插入图片描述

3.点击登录后,通过钉钉配置的回调接口,跳转自己的平台首页
在这里插入图片描述


相关文章
|
9天前
|
前端开发 JavaScript UED
探索Python Django中的WebSocket集成:为前后端分离应用添加实时通信功能
通过在Django项目中集成Channels和WebSocket,我们能够为前后端分离的应用添加实时通信功能,实现诸如在线聊天、实时数据更新等交互式场景。这不仅增强了应用的功能性,也提升了用户体验。随着实时Web应用的日益普及,掌握Django Channels和WebSocket的集成将为开发者开启新的可能性,推动Web应用的发展迈向更高层次的实时性和交互性。
34 1
|
23天前
|
机器学习/深度学习 人工智能 算法
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
文本分类识别系统。本系统使用Python作为主要开发语言,首先收集了10种中文文本数据集("体育类", "财经类", "房产类", "家居类", "教育类", "科技类", "时尚类", "时政类", "游戏类", "娱乐类"),然后基于TensorFlow搭建CNN卷积神经网络算法模型。通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型,并保存为本地的h5格式。然后使用Django开发Web网页端操作界面,实现用户上传一段文本识别其所属的类别。
48 1
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
23天前
|
机器学习/深度学习 人工智能 算法
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台。果蔬识别系统,本系统使用Python作为主要开发语言,通过收集了12种常见的水果和蔬菜('土豆', '圣女果', '大白菜', '大葱', '梨', '胡萝卜', '芒果', '苹果', '西红柿', '韭菜', '香蕉', '黄瓜'),然后基于TensorFlow库搭建CNN卷积神经网络算法模型,然后对数据集进行训练,最后得到一个识别精度较高的算法模型,然后将其保存为h5格式的本地文件方便后期调用。再使用Django框架搭建Web网页平台操作界面,实现用户上传一张果蔬图片识别其名称。
41 0
【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
2月前
|
存储 关系型数据库 MySQL
基于python django 医院管理系统,多用户功能,包括管理员、用户、医生,数据库MySQL
本文介绍了一个基于Python Django框架开发的医院管理系统,该系统设计了管理员、用户和医生三个角色,具备多用户功能,并使用MySQL数据库进行数据存储和管理。
基于python django 医院管理系统,多用户功能,包括管理员、用户、医生,数据库MySQL
|
2月前
|
机器学习/深度学习 前端开发 数据挖掘
基于Python Django的房价数据分析平台,包括大屏和后台数据管理,有线性、向量机、梯度提升树、bp神经网络等模型
本文介绍了一个基于Python Django框架开发的房价数据分析平台,该平台集成了多种机器学习模型,包括线性回归、SVM、GBDT和BP神经网络,用于房价预测和市场分析,同时提供了前端大屏展示和后台数据管理功能。
|
3月前
|
前端开发 JavaScript API
【Django+Vue3 线上教育平台项目实战】构建课程详情页与集成视频播放功能
随着数字化教育的兴起,构建一个高效、用户友好的线上教育平台至关重要。本文将探讨如何使用Django与Vue.js 3结合,实现一个包含课程列表和课程详情页(含视频播放功能)的线上教育平台部分。本文主要介绍了如何设计数据库模型、处理数据查询、构建动态前端界面,并集成视频播放功能,为用户带来流畅的学习体验。
【Django+Vue3 线上教育平台项目实战】构建课程详情页与集成视频播放功能
|
2月前
|
数据可视化 安全 前端开发
基于Django的美团药品数据分析与可视化系统,有多用户功能,可增删改查数据
本文介绍了一个基于Django框架开发的美团药品数据分析与可视化系统,该系统具备多用户功能,支持数据的增删改查操作,并采用MySQL、pandas、echarts和bootstrap技术栈,为用户提供了一个高效、安全且实用的药品数据管理和分析平台。
基于Django的美团药品数据分析与可视化系统,有多用户功能,可增删改查数据
|
2月前
|
存储 监控 开发工具
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
46 0
|
3月前
|
存储 数据库 文件存储
掌握Django文件处理:一步步构建上传功能
掌握Django文件处理:一步步构建上传功能
51 3
|
2月前
|
机器学习/深度学习 数据采集 数据可视化
基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台
本文介绍了一个基于Python Django框架和Bootstrap前端技术,集成了机器学习算法和数据可视化的招聘数据分析与可视化系统,该系统通过爬虫技术获取职位信息,并使用多种机器学习模型进行薪资预测、职位匹配和趋势分析,提供了一个直观的可视化大屏和后台管理系统,以优化招聘策略并提升决策质量。
137 4