当前位置: 首页 > news >正文

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

在这里插入图片描述


文章目录

  • 前言
  • 一、几个关键概念
    • 1.HTTP无状态性
    • 2.Session机制
    • 3.Token认证
    • 4.JWT
  • 二、通过手机号验证码登录
    • 1.前端短信登录界面
    • 2.发送短信接口与短信登录接口
    • 3.Vue 设置interceptors拦截器
    • 4. 服务端验证采用自定义中间件方式实现
    • 5. 操作流程及效果图如下:
  • 三、通过第三方平台进行登录
    • 1.准备工作
    • 2.前端钉钉登录界面
    • 3.后端逻辑处理
    • 4.操作流程及效果图如下:


前言

    在当今的数字化时代,用户认证是任何在线服务安全性的基石。本文将简明扼要地介绍登录注册流程中的核心概念: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 timeclass 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); //客户端存储useridlocalStorage.setItem('token',res.data.token); //客户端存储tokenreturn 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 = mobiledatas = (sms_code, )resp = sdk.sendMessage(tid, mobile, datas)data = json.loads(resp) #json-->对象print(data)if data['statusCode'] == '000000':return Truereturn False

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

# 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 tokentoken = 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

src\http\index.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存储的tokenlet 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 timeclass PermitionMiddleware(MiddlewareMixin):def process_request(self, request):# 1.定义白名单,在登录前需要操作的接口放到白名单中wlist = ['/register/', '/login/', '/sendsms/']# 获取当前的urlpath = 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已经过期不能操作"})#是否退出,退出时存tokenvalue = 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,跳转到前端页面
# 钉钉三方登录接口
class DingTalkLogin(APIView):def get(self, request):from urllib.parse import quoteparams = [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获取用户accessTokendata = {"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.userSfuser.token = accessTokenSfuser.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()) + 3600retoken = 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.点击登录后,通过钉钉配置的回调接口,跳转自己的平台首页
在这里插入图片描述


在这里插入图片描述

相关文章:

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

文章目录 前言一、几个关键概念1.HTTP无状态性2.Session机制3.Token认证4.JWT 二、通过手机号验证码登录1.前端短信登录界面2.发送短信接口与短信登录接口3.Vue 设置interceptors拦截器4. 服务端验证采用自定义中间件方式实现5. 操作流程及效果图如下&#xff1a; 三、通过第三…...

关于HBase、Phoenix、Flume、Maxwell 和 Flink

组件协同&#xff1a; HBase HBase 是一个分布式的、列存储的NoSQL数据库&#xff0c;它基于Google的Bigtable设计&#xff0c;特别适合存储海量的、稀疏的、非结构化或半结构化数据。HBase 提供了低延迟的随机读写能力&#xff0c;但其原生接口和数据模型较为复杂&#xff0…...

centos7停止维护,可替代的操作系统

CentOS 7 将在 2024 年 6 月 30 日停止维护。如果你目前正在使用 CentOS 7&#xff0c;可以考虑以下几种替代的操作系统&#xff1a; 1. CentOS Stream CentOS Stream 是 CentOS 项目的一个新版本&#xff0c;它提供了一个滚动发布的 Linux 发行版。CentOS Stream 位于 Fedor…...

andon系统在电力设备工管理中起到那些作用与价值

安灯系统&#xff0c;作为精益制造执行中的一个核心工具&#xff0c;在电力设备工厂车间管理中发挥着不可替代的作用&#xff0c;它能够实现生产透明管理&#xff0c;为工厂高效运作提供强大的支撑。本文将从安灯系统的功能、应用场景和价值三个方面&#xff0c;深入探讨其在电…...

消息队列-RabbitMQ

消息队列-RabbitMQ 1、RabbitMQ是什么?2、RabbitMQ的业务场景有哪些?3、RabbitMQ中有哪基本概念?4、RabbitMQ有哪些工作模式?5、如何保证RabbitMQ消息顺序性?6、RabbitMQ消息如何分发?7、RabbitMQ消息怎么路由?8、为什么会产生重复消费?如何保证消息不被重复消费(如何…...

Elasticsearch(ES)集群监控

Elasticsearch(ES)集群监控 在Elasticsearch中&#xff0c;监控集群的健康状况、性能和运行指标是至关重要的。以下是一些常用的Elasticsearch监控工具和API的例子&#xff1a; 使用Elasticsearch自带的API来获取集群健康状态、节点信息和统计信息。 # 获取集群健康状况 curl…...

图像处理:使用 OpenCV-Python 卡通化你的图像(2)

一、说明 在图像处理领域&#xff0c;将图像卡通化是一种新趋势。人们使用不同的应用程序将他们的图像转换为卡通图像。如今&#xff0c;玩弄图像是许多人的爱好。人们通常会点击图片并添加滤镜或使用不同的东西自定义图像并将其发布到社交媒体上。但我们是程序员&#xff0c;…...

淘宝扭蛋机小程序:旋转惊喜,开启购物新篇章!

在追求创新与惊喜的购物时代&#xff0c;淘宝再次引领潮流&#xff0c;精心打造——淘宝扭蛋机小程序&#xff0c;为您的购物之旅增添一抹不同寻常的色彩。这不仅仅是一个购物工具&#xff0c;更是一个充满趣味、互动与惊喜的宝藏盒子&#xff0c;等待您来探索与发现。 【旋转…...

JAVA零基础小白自学日志——第十七天

文章目录 1.方法的覆写2.类的层次结构3.如何判定正确的继承顺序4.如何判断子类继承&#xff08;继承这个词我始终觉得很变扭&#xff09;了父类的什么5.继承关系的实质6.关键字&#xff1a;super 和 this[1].this关键字&#xff08;1&#xff09;this关键字调用本类属性&#x…...

electron中app.whenReady()和app.on(‘ready‘)的区别

app.whenReady和app.on(‘ready’)都是用于在Electron应用程序中处理初始化完成事件的方法。app.whenReady是一个返回Promise的方法&#xff0c;它会在应用程序准备好创建窗口时解决。一旦app.whenReady被调用&#xff0c;就可以安全地创建窗口&#xff0c;因为此时Electron的初…...

技术速递|Let’s Learn .NET Aspire – 开始您的云原生之旅!

作者&#xff1a;James Montemagno 排版&#xff1a;Alan Wang Let’s Learn .NET 是我们全球性的直播学习活动。在过去 3 年里&#xff0c;来自世界各地的开发人员与团队成员一起学习最新的 .NET 技术&#xff0c;并参加现场研讨会学习如何使用它&#xff01;最重要的是&#…...

JSONNode树形解析或流式解析

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 什么是JSONNode&#xff1f; JSONNode是一个用于处理JSON数据的数据结构&#xff0c;它提供了一种简单、灵活、高效的方式来操作JSON数据。JSONNode可以看作是一个树形结构&#xff0c;其中每个节点都可以包含一个值…...

自制迷宫游戏 c++

竞赛的同时也不能忘记娱乐&#xff0c;劳逸结合&#xff0c;我们自研了迷宫游戏,只能在DEV C 运行哦 #include<bits/stdc.h> #include<iomanip> #include<iostream> #include<cstdlib> #include<ctime> #include<conio.h> #include<win…...

基于复旦微JFMQL100TAI的全国产化FPGA+AI人工智能异构计算平台,兼容XC7Z045-2FFG900I

基于上海复旦微电子FMQL45T900的全国产化ARM核心板。该核心板将复旦微的FMQL45T900&#xff08;与XILINX的XC7Z045-2FFG900I兼容&#xff09;的最小系统集成在了一个87*117mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;能够快速的搭建起…...

【数学建模】技术革新——Lingo的使用超详解

目录 基础知识 1. 变量声明 示例 2. 常量声明 语法格式 示例 3. 目标函数 语法格式 示例 4. 约束条件 语法格式 示例 5. 完整的Lingo模型示例 示例 解释 6. 整数变量声明 语法格式 示例 7. 非线性规划 示例 8. 多目标优化 语法格式 示例 9. 数据输入与…...

LLM-阿里 DashVector + langchain self-querying retriever 优化 RAG 实践【Query 优化】

文章目录 前言self querying 简介代码实现总结 前言 现在比较流行的 RAG 检索就是通过大模型 embedding 算法将数据嵌入向量数据库中&#xff0c;然后在将用户的查询向量化&#xff0c;从向量数据库中召回相似性数据&#xff0c;构造成 context template, 放到 LLM 中进行查询…...

【python】PyQt5的窗口界面的各种交互逻辑实现,轻松掌控图形化界面程序

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

DockerCompose介绍,安装,使用

DockerCompose 1、Compose介绍 将单机服务-通过Dockerfile 构建为镜像 -docker run 成为一个服务 user 8080 net 7000 pay 8181 admin 5000 监控 .... docker run 单机版、一个个容器启动和停止问题&#xff1a; 前面我们使用Docker的时候&#xff0c;定义 Dockerfil…...

N叉树的前序遍历

Problem: 589. N 叉树的前序遍历 文章目录 思路解题过程Code 思路 前序遍历&#xff0c;遇到空节点返回 解题过程 对每个节点进行遍历 Code /* // Definition for a Node. class Node { public:int val;vector<Node*> children;Node() {}Node(int _val) {val _val;}Nod…...

Linux C++ 054-设计模式之外观模式

Linux C 054-设计模式之外观模式 本节关键字&#xff1a;Linux、C、设计模式、外观模式 相关库函数&#xff1a; 概念 外观模式&#xff08;Facade&#xff09;&#xff0c;亦称“过程模式”。主张按照描述和判断资料来评价课程&#xff0c;关键的活动是在课程实施的全过程中…...

Linux - 冯-诺依曼体系结构、初始操作系统

目录 冯•诺依曼体系 结构推导 内存提高效率的方法 数据的流动过程 体系结构相关知识 初始操作系统 定位 设计目的 操作系统之上之下分别有什么 管理精髓&#xff1a;先描述&#xff0c;再组织 冯•诺依曼体系 结构推导 计算机基本工作流程图大致如下&#xff1a; 输入设备&a…...

成功适配!极验设备指纹HarmonyOS 鸿蒙版官方下载

近日&#xff0c;华为开发者大会&#xff08;HDC 2024&#xff09;在东莞召开。在大会开幕日的首场主题演讲中&#xff0c;华为宣布当前已有TOP5000应用成为鸿蒙原生应用&#xff0c;350&#xff0b;SDK已适配HarmonyOS NEXT版本。其中&#xff0c;极验作为其重要伙伴&#xff…...

【C++】字符串学习 知识点+代码记录

一.知识点总结 1. C风格字符串&#xff08;字符数组&#xff09; 字符数组存储字符串&#xff1a;C风格的字符串实际上是字符数组&#xff0c;以空字符\0作为结尾标志。字符串字面量与字符数组&#xff1a;字符串字面量如"Hello"被编译器视为const char*类型&#…...

尝试理解docker网络通信逻辑

一、docker是什么 Docker本质是一个进程,宿主机通过namespace隔离机制提供进程需要运行基础环境&#xff0c;并且通过Cgroup限制进程调用资源。Docker的隔离机制包括 network隔离&#xff0c;此次主要探讨网络隔离mount隔离hostname隔离user隔离pid隔离进程通信隔离 二、doc…...

数据仓库哈哈

数据仓库 基本概念数据库&#xff08;database&#xff09;和数据仓库&#xff08;Data Warehouse&#xff09;的异同 整体架构分层架构方法论ER模型&#xff08;建模理论&#xff09;维度模型 何为分层第一层&#xff1a;数据源&#xff08;ODS ER模型&#xff09;设计要点日志…...

K最近邻(K-Nearest Neighbors, KNN)

K最近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;理论知识推导 KNN算法是一个简单且直观的分类和回归方法&#xff0c;其基本思想是&#xff1a;给定一个样本点&#xff0c;找到训练集中与其最近的K个样本点&#xff0c;根据这些样本点的类别&#xff08;分类问题&am…...

深度学习损失计算

文章目录 深度学习损失计算1.如何计算当前epoch的损失&#xff1f;2.为什么要计算样本平均损失&#xff0c;而不是计算批次平均损失&#xff1f; 深度学习损失计算 1.如何计算当前epoch的损失&#xff1f; 深度学习中的损失计算&#xff0c;通常为数据集的平均损失&#xff0…...

论文翻译:通过云计算对联网多智能体系统进行预测控制

通过云计算对联网多智能体系统进行预测控制 文章目录 通过云计算对联网多智能体系统进行预测控制摘要前言通过云计算实现联网的多智能体控制系统网络化多智能体系统的云预测控制器设计云预测控制系统的稳定性和一致性分析例子结论 摘要 本文研究了基于云计算的网络化多智能体预…...

Java核心(五)多线程

线程并行的逻辑 一个线程问题 起手先来看一个线程问题&#xff1a; public class NumberExample {private int cnt 0;public void add() {cnt;}public int get() {return cnt;} }public static void main(String[] args) throws InterruptedException {final int threadSiz…...

IDEA快速生成项目树形结构图

下图用的IDEA工具&#xff0c;但我觉得WebStorm 应该也可以 文章目录 进入项目根目录下&#xff0c;进入cmd输入如下指令&#xff1a; 只有文件夹 tree . > list.txt 包括文件夹和文件 tree /f . > list.txt 还可以为相关包路径加上注释...