【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.登录开发者后台
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. 操作流程及效果图如下: 三、通过第三…...
关于HBase、Phoenix、Flume、Maxwell 和 Flink
组件协同: HBase HBase 是一个分布式的、列存储的NoSQL数据库,它基于Google的Bigtable设计,特别适合存储海量的、稀疏的、非结构化或半结构化数据。HBase 提供了低延迟的随机读写能力,但其原生接口和数据模型较为复杂࿰…...
centos7停止维护,可替代的操作系统
CentOS 7 将在 2024 年 6 月 30 日停止维护。如果你目前正在使用 CentOS 7,可以考虑以下几种替代的操作系统: 1. CentOS Stream CentOS Stream 是 CentOS 项目的一个新版本,它提供了一个滚动发布的 Linux 发行版。CentOS Stream 位于 Fedor…...

andon系统在电力设备工管理中起到那些作用与价值
安灯系统,作为精益制造执行中的一个核心工具,在电力设备工厂车间管理中发挥着不可替代的作用,它能够实现生产透明管理,为工厂高效运作提供强大的支撑。本文将从安灯系统的功能、应用场景和价值三个方面,深入探讨其在电…...
消息队列-RabbitMQ
消息队列-RabbitMQ 1、RabbitMQ是什么?2、RabbitMQ的业务场景有哪些?3、RabbitMQ中有哪基本概念?4、RabbitMQ有哪些工作模式?5、如何保证RabbitMQ消息顺序性?6、RabbitMQ消息如何分发?7、RabbitMQ消息怎么路由?8、为什么会产生重复消费?如何保证消息不被重复消费(如何…...
Elasticsearch(ES)集群监控
Elasticsearch(ES)集群监控 在Elasticsearch中,监控集群的健康状况、性能和运行指标是至关重要的。以下是一些常用的Elasticsearch监控工具和API的例子: 使用Elasticsearch自带的API来获取集群健康状态、节点信息和统计信息。 # 获取集群健康状况 curl…...

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

淘宝扭蛋机小程序:旋转惊喜,开启购物新篇章!
在追求创新与惊喜的购物时代,淘宝再次引领潮流,精心打造——淘宝扭蛋机小程序,为您的购物之旅增添一抹不同寻常的色彩。这不仅仅是一个购物工具,更是一个充满趣味、互动与惊喜的宝藏盒子,等待您来探索与发现。 【旋转…...
JAVA零基础小白自学日志——第十七天
文章目录 1.方法的覆写2.类的层次结构3.如何判定正确的继承顺序4.如何判断子类继承(继承这个词我始终觉得很变扭)了父类的什么5.继承关系的实质6.关键字:super 和 this[1].this关键字(1)this关键字调用本类属性&#x…...
electron中app.whenReady()和app.on(‘ready‘)的区别
app.whenReady和app.on(‘ready’)都是用于在Electron应用程序中处理初始化完成事件的方法。app.whenReady是一个返回Promise的方法,它会在应用程序准备好创建窗口时解决。一旦app.whenReady被调用,就可以安全地创建窗口,因为此时Electron的初…...

技术速递|Let’s Learn .NET Aspire – 开始您的云原生之旅!
作者:James Montemagno 排版:Alan Wang Let’s Learn .NET 是我们全球性的直播学习活动。在过去 3 年里,来自世界各地的开发人员与团队成员一起学习最新的 .NET 技术,并参加现场研讨会学习如何使用它!最重要的是&#…...

JSONNode树形解析或流式解析
哈喽,大家好,我是木头左! 什么是JSONNode? JSONNode是一个用于处理JSON数据的数据结构,它提供了一种简单、灵活、高效的方式来操作JSON数据。JSONNode可以看作是一个树形结构,其中每个节点都可以包含一个值…...
自制迷宫游戏 c++
竞赛的同时也不能忘记娱乐,劳逸结合,我们自研了迷宫游戏,只能在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(与XILINX的XC7Z045-2FFG900I兼容)的最小系统集成在了一个87*117mm的核心板上,可以作为一个核心模块,进行功能性扩展,能够快速的搭建起…...

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

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

【python】PyQt5的窗口界面的各种交互逻辑实现,轻松掌控图形化界面程序
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

DockerCompose介绍,安装,使用
DockerCompose 1、Compose介绍 将单机服务-通过Dockerfile 构建为镜像 -docker run 成为一个服务 user 8080 net 7000 pay 8181 admin 5000 监控 .... docker run 单机版、一个个容器启动和停止问题: 前面我们使用Docker的时候,定义 Dockerfil…...
N叉树的前序遍历
Problem: 589. N 叉树的前序遍历 文章目录 思路解题过程Code 思路 前序遍历,遇到空节点返回 解题过程 对每个节点进行遍历 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-设计模式之外观模式 本节关键字:Linux、C、设计模式、外观模式 相关库函数: 概念 外观模式(Facade),亦称“过程模式”。主张按照描述和判断资料来评价课程,关键的活动是在课程实施的全过程中…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...