vue3+threejs+koa可视化项目——实现登录注册(第三步)

文章目录

    • ⭐前言
      • 💖往期node系列文章
      • 💖threejs系列相关文章
      • 💖vue3+threejs系列
    • ⭐koa后端登录注册逻辑(jwt)
      • 💖 koa登录注册
    • ⭐vue3前端登录注册权限控制
      • 💖 登录页面
      • 💖 注册页面
    • ⭐总结
    • ⭐结束

yma16-logo

⭐前言

大家好,我是yma16,本文分享关于 vue3+threejs+koa可视化项目——实现登录注册。

jwt登录注册
JWT(JSON Web Token)是一种标准的身份验证和授权解决方案,它通过使用JSON格式的令牌来实现用户的身份验证和授权,避免了传统的基于会话的身份验证方案的一些问题。

JWT登录注册的原理如下:

  1. 注册:用户在注册时提供用户名和密码,服务器将用户信息保存在数据库中。密码通常需要进行哈希处理,以增加安全性。

  2. 登录:用户提供用户名和密码进行身份验证时,服务器验证用户名和密码是否匹配数据库中的记录。如果匹配成功,服务器会为该用户生成一个JWT令牌。

  3. 令牌生成:JWT由三部分组成,分别是头部(header)、载荷(payload)和签名(signature)。头部包含算法和令牌类型等信息,载荷包含用户的身份信息和其他自定义信息,签名用于验证令牌的合法性。

  4. 令牌签名:服务器使用服务器端的私钥对头部和载荷进行签名,生成签名部分。客户端接收到令牌后,可以使用服务器端的公钥进行验证,确保令牌没有被修改过。

  5. 令牌的验证和使用:客户端在每次请求时将令牌作为请求的一部分(通常是在请求头的"Authorization"字段中)发送到服务器。服务器通过解析令牌的签名部分并验证签名的合法性,确定令牌的有效性。如果通过验证,服务器可以根据令牌中的载荷部分进行用户的身份验证和授权等操作。

  6. 令牌的刷新:JWT令牌通常有一个过期时间,当令牌过期时,客户端需要重新获取新的令牌。在令牌过期之前,客户端可以使用刷新令牌来获取新的令牌,而无需重新进行身份验证。

通过JWT登录注册的方式,可以实现无状态的身份验证和授权,减少服务器的负担和数据库的访问频率。同时,JWT还可以跨多个服务进行验证和授权,提高了系统的可扩展性和安全性。

💖往期node系列文章

node_windows环境变量配置
node_npm发布包
linux_配置node
node_nvm安装配置
node笔记_http服务搭建(渲染html、json)
node笔记_读文件
node笔记_写文件
node笔记_连接mysql实现crud
node笔记_formidable实现前后端联调的文件上传
node笔记_koa框架介绍
node_koa路由
node_生成目录
node_读写excel
node笔记_读取目录的文件
node笔记——调用免费qq的smtp发送html格式邮箱
node实战——搭建带swagger接口文档的后端koa项目(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)
node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)

💖threejs系列相关文章

THREE实战1_代码重构点、线、面
THREE实战2_正交投影相机与透视相机
THREE实战3_理解光源
THREE实战4_3D纹理
THREE实战5_canvans纹理
THREE实战6_加载fbx模型

💖vue3+threejs系列

vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
vue3+threejs可视化项目——引入threejs加载钢铁侠模型(第二步)

⭐koa后端登录注册逻辑(jwt)

用户表配置(用户名、密码、token)

/*Navicat Premium Data TransferSource Server         : localhostSource Server Type    : MySQLSource Server Version : 80016Source Host           : localhost:3306Source Schema         : threejs_dataTarget Server Type    : MySQLTarget Server Version : 80016File Encoding         : 65001Date: 28/01/2024 23:56:13
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'id',`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '加密的密码',`real_pwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '真实密码',`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'token',`lasted_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最近登录时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

💖 koa登录注册

逻辑实现

const Router = require('koa-router');
const router = new Router();
const {execMysql}=require('../../utils/mysql/index')
const {decrypt}=require('../../utils/aes/index')
const jwtToken = require("jsonwebtoken");const {getRedisKey,setRedisConfig}=require('../../utils/redis/index');//appKey
const {appKey}=require('../../common/const')// 唯一字符串
function uuid() {return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {var r = Math.random() * 16 | 0,v = c == 'x' ? r : (r & 0x3 | 0x8);return v.toString(16);});
}// 当前时间
const  getCurrentTime=() =>{const now = new Date()const year = now.getFullYear()const month = now.getMonth()const date = now.getDate()const hour = now.getHours()const minutes = now.getMinutes()const second = now.getSeconds()const formatNum = (n) => {return n > 9 ? n.toString() : '0' + n}return `${year}-${formatNum(month + 1)}-${formatNum(date)} ${formatNum(hour)}:${formatNum(minutes)}:${formatNum(second)}`
}// 注册
router.post('/register', async (ctx) => {try{// 解析参数const bodyParams =  ctx.request.bodyconst {username,password,emailCode} = bodyParams;console.log('emailCode',emailCode)console.log('emailCode',emailCode)if(!username||!password){return ctx.body = {code: 0 ,msg:'username or password is null'};}const emailRedisCode=await getRedisKey(username)console.log('emailRedisCode',emailRedisCode)if(emailCode!==emailRedisCode){return ctx.body = {code: 0 ,msg:'email code is error'};}// 查询重复const search=await execMysql(`select count(1) as total from user where username='${username}';`)console.log('search',search)if(search[0].total>0){return ctx.body = {code: 0 ,msg:'user is exist'};}// id 唯一字符const id= uuid()const create_time=getCurrentTime()console.log('password',password)const real_pwd=await decrypt(password)console.log('real_pwd',real_pwd)// 插入 数据const createRes=await execMysql(`INSERT INTO user (id,username,password,real_pwd,create_time) VALUES ('${id}', '${username}','${password}','${real_pwd}','${create_time}');`)// 更新token update_timeconst token=jwtToken.sign({username,password},appKey, // secret{ expiresIn: 24 * 60 * 60 } // 60 * 60 s)const update_time=getCurrentTime()const tokenRes=await execMysql(`update user set token='${token}', update_time='${update_time}' where username='${username}';`)ctx.body = {code:200,data:{createSqlData:createRes,tokenSqlData:tokenRes},msg:' insert success',token:token};}catch (e) {ctx.body = {code:0,msg:JSON.stringify(e)};}
});// 获取token
router.post('/token/gen', async (ctx) => {try{// 解析参数const bodyParams =  ctx.request.bodyconst {username,password} = bodyParams;const real_pwd=await decrypt(password);// 查询 用户const search=await execMysql(`select count(1) as total from user where username='${username}' and real_pwd='${real_pwd}';`)if(search[0].total>0){// 更新token update_timeconst token=jwtToken.sign({username,password},appKey, // secret{ expiresIn: 24 * 60 * 60 } // 60 * 60 s)const update_time=getCurrentTime()// 更新tokenconst tokenRes=await execMysql(`update user set token='${token}', update_time='${update_time}' where username='${username}';`)// 配置tokenconst AUTHORIZATION='Authorization'ctx.set(AUTHORIZATION,token)return ctx.body = {code:200,msg:'login success',token:token};}ctx.body = {code:0,msg:' login fail',};}catch (e) {ctx.body = {code:0,msg:e};}
});// token 登录
router.post('/token/login',async (ctx) => {try{// 解析参数const bodyParams =  ctx.request.bodyconst {token} = bodyParams;const payload = jwtToken.verify(token, appKey);const {username,password} =payloadconst real_pwd=await decrypt(password);// 查询 用户const search=await execMysql(`select count(1) as total from user where username='${username}' and real_pwd='${real_pwd}';`)console.log(search)if(search[0].total>0){const last_login_time=getCurrentTime()// last_login_time  登录时间const tokenRes=await execMysql(`update user set lasted_login_time='${last_login_time}' where username='${username}' and password='${password}';`)return ctx.body = {code:200,msg:'login success',data:{username}};}ctx.body = {code:0,msg:' login fail',};}catch (e) {console.log('e',e)ctx.body = {code:0,msg:JSON.stringify(e)};}
})module.exports = router;

⭐vue3前端登录注册权限控制

💖 登录页面

login/index.vue

<template><div class="container"><div class="loginUser-container"><div class="loginUser-title"></div><a-form:model="state.formState":label-col="state.layoutConfig.labelCol":wrapper-col="state.layoutConfig.wrapperCol":rules="state.formRule"ref="formRef"layout="vertical"autocomplete="off"><a-form-item label="账号" name="username"><a-inputv-model:value="state.formState.username"allowClearplaceholder="账号":disabled="state.spinning"/></a-form-item><a-form-item label="密码" name="password"><a-input-passwordv-model:value="state.formState.password":disabled="state.spinning"allowClearplaceholder="请输入密码"/></a-form-item><a-form-item name="remember" :wrapper-col="state.wrapperCol"><a-checkboxv-model:checked="state.formState.remember":disabled="state.spinning">记住密码</a-checkbox></a-form-item><a-form-item :wrapper-col="state.submitWrapperCol" class="submit-box"><a-buttontype="primary"html-type="submit"@click="loginAction":loading="state.spinning"style="width: 100%; font-size: 16px; font-weight: bolder">登录</a-button></a-form-item></a-form><div class="description"><span class="description-prefix">没账号?</span><span@click="jumpRegister"class="description-after":disabled="state.spinning">去注册</span></div></div></div>
</template>
<script lang="ts" setup>import { reactive, ref, onMounted } from "vue";import { useRouter } from "vue-router";import { useStore } from "vuex";import { message } from "ant-design-vue";import {aes} from '@/utils/index'import type { FormInstance } from "ant-design-vue";interface FormStateType {username: string;password: string;remember: boolean;}interface FormRuleType {username: Object;password: Object;}interface stateType {formState: FormStateType;formRule: FormRuleType;layoutConfig: any;wrapperCol: any;submitWrapperCol: any;spinning: boolean;backgroundImgUrl: string;}// 路由const router = useRouter();//storeconst store = useStore();const formRef = ref<FormInstance>();const state: stateType = reactive({formState: {username: "",password: "",remember: false,},spinning: false,formRule: {username: [{ required: true, message: "请输入账号!" }],password: [{ required: true, message: "请输入密码!" }],},layoutConfig: {labelCol: {span: 8,},wrapperCol: {span: 24,},},wrapperCol: { offset: 0, span: 24 },submitWrapperCol: { offset: 0, span: 24 },backgroundImgUrl:"http://www.yongma16.xyz/staticFile/common/img/background.png",});/*** 初始化表单内容*/const initForm = () => {const userInfoItem: any = window.localStorage.getItem("userInfo");interface userInfoType {username: string;password: string;remember: boolean;}const userInfo: userInfoType = userInfoItem? JSON.parse(userInfoItem): {username: "",password: "",remember: false,};if (userInfo.username && userInfo.password) {state.formState.username = userInfo.username;state.formState.password = aes.decrypt(userInfo.password);state.formState.remember = userInfo.remember;}};/*** 前往注册!*/const jumpRegister = () => {// 带 hash,结果是 /about#teamrouter.push({ path: "/register" });};/*** 前往home!*/const jumpHome = () => {// 带 hash,结果是 /about#teamrouter.push({ path: "/" });};/*** 记住密码* @param params*/const rememberAction = (params: Object) => {window.localStorage.setItem("userInfo", JSON.stringify(params));};/*** 登录*/const loginAction = () => {if (formRef.value) {formRef.value.validate().then(async (res: any) => {state.spinning = true;const params = {username: state.formState.username,password: aes.encrypt(state.formState.password),};if (state.formState.remember) {rememberAction({ ...params, remember: state.formState.remember });}try {console.log('登录',params)// @ts-ignoreawait store.dispatch("user/getUserTokenAction",params);// 跳转setTimeout(() => {jumpHome();}, 500);state.spinning = false;} catch (r: any) {message.error(JSON.stringify(r));state.spinning = false;throw Error(r);}});}};onMounted(() => {//初始化initForm();});
</script><style lang="less">.background {/*background: #1890ff;  !* fallback for old browsers *!*//*background: -webkit-linear-gradient(to top, #000C40, #F0F2F0);  !* Chrome 10-25, Safari 5.1-6 *!*//*background: linear-gradient(to top, #000C40, #F0F2F0); !* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ *!*//*background-image: url("http://yongma16.xyz/staticFile/common/img/background.png");*//*background-repeat: no-repeat;*//*background-size: 100%;*/}.container {/*background: #262626;*/background-clip: border-box;position: absolute;width: 100vw;height: 100vh;.background();}.loginUser-container {position: absolute;min-width: 400px;min-height: 350px;left: 50%;top: 50%;transform: translate(-50%, -50%);}.submit-box {text-align: center;width: 100%;margin: 0 auto;}.loginUser-container {background-color: rgba(255, 255, 255, 0.8);border-radius: 10px;padding: 0 20px;}.loginUser-title {margin-top: 20px;width: 100%;text-align: center;font-weight: bolder;font-size: 16px;}.description {margin-top: 20px;width: 100%;text-align: center;.description-after {color: #1890ff;cursor: pointer;}}
</style>

登录页面
在这里插入图片描述

💖 注册页面

register/index.vue

<template><a-spin tip="登录中..." :spinning="state.spinning"><!--    <div--><!--      class="container"--><!--      :style="{--><!--        backgroundImage:url(state.backgroundImgUrl),--><!--      }"--><!--    >--><div class="container"><div class="register-container"><div class="register-title"></div><a-form:model="state.formState":label-col="state.layoutConfig.labelCol":wrapper-col="state.layoutConfig.wrapperCol":rules="state.formRule"ref="formRef"layout="vertical"autocomplete="off"><a-row><a-col :span="16"><a-form-item label="邮箱" name="username"><a-inputv-model:value="state.formState.username"allowClearplaceholder="请输入邮箱"/></a-form-item></a-col><a-col :span="8"><a-form-item style="margin-top:31.53px;text-align: right"><a-button @click="sendEmail" type="primary" :loading="state.loadingEmailCode" >{{state.awaitTime>0?`${state.awaitTime}s`:'发送验证码'}}</a-button></a-form-item></a-col></a-row><a-form-item label="验证码" name="code"><a-input:disabled="state.isErrorEmail"v-model:value="state.formState.code"allowClearplaceholder="请输入验证码"/></a-form-item><a-form-item label="密码" name="password"><a-input-password:disabled="state.isErrorEmail"v-model:value="state.formState.password"allowClearplaceholder="请输入验证码"/></a-form-item><a-form-item label="确认密码" name="passwordBack"><a-input-password:disabled="state.isErrorEmail"v-model:value="state.formState.passwordBack"allowClearplaceholder="请确认密码"/></a-form-item><a-form-item :wrapper-col="state.submitWrapperCol" class="submit-box"><a-buttontype="primary"html-type="submit"@click="registerAction":loading="state.spinning"style="width: 100%; font-weight: bolder">注册</a-button></a-form-item></a-form><div class="description"><span class="description-prefix">有账号?</span><span @click="jumpLogin" class="description-after">去登录</span></div></div></div></a-spin>
</template>
<script lang="ts" setup>import { reactive, ref } from "vue";import { useRouter } from "vue-router";import { message } from "ant-design-vue";import { registerUser,getEmailCode } from "@/service/user/index";import {aes} from '@/utils/index'import type { Rule } from "ant-design-vue/es/form";import type { FormInstance } from "ant-design-vue";import { useStore } from "vuex";interface FormStateType {username: string;password: string;passwordBack: string;code:string;remember: boolean;}interface FormRuleType {username: Object;password: Object;}interface stateType {formState: FormStateType;formRule: FormRuleType;layoutConfig: any;wrapperCol: any;submitWrapperCol: any;spinning: boolean;backgroundImgUrl: string;isErrorEmail:boolean,remoteEmailCode:string,loadingEmailCode:boolean,awaitTime:number}// 路由const router = useRouter();//storeconst store = useStore();const formRef = ref<FormInstance>();const state: stateType = reactive({formState: {code:'',username: "1432448610@qq.com",password: "",passwordBack: "",remember: false,},spinning: false,formRule: {username: [{ required: true, message: "请输入邮箱!" },{validator:validatorEmail,trigger:'blur'}],code: [{ required: false, message: "请输入验证码!" }],password: [{ required: true, message: "请输入密码!" }],passwordBack: [{ required: true },{ validator: validatePass, trigger: "blur" },],},layoutConfig: {labelCol: {span: 8,},wrapperCol: {span: 24,},},awaitTime:-1,remoteEmailCode:'',isErrorEmail:true,loadingEmailCode:false,wrapperCol: { offset: 0, span: 24 },submitWrapperCol: { offset: 0, span: 24 },backgroundImgUrl:"http://www.yongma16.xyz/staticFile/common/img/background.png",});// sleepconst sleep=(delay:number)=>{return new Promise(resolve=>setTimeout(resolve,delay*1000))}/*** 前往登录!*/const jumpLogin = () => {// 带 hash,结果是 /about#teamrouter.push({ path: "/login" });};// 确认密码async function validatePass(_rule: Rule, value: string) {if (value === "") {return Promise.reject("请确认密码!");} else if (value !== state.formState.password) {return Promise.reject("密码不一致!");} else {return Promise.resolve();}}// 校验邮箱async function validatorEmail(_rule: Rule, value: string) {if (value === "") {state.isErrorEmail=truereturn Promise.reject("请输入邮箱!");} else {const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;if (!reg.test(value)) {state.isErrorEmail=truereturn Promise.reject("邮箱格式不正确!");}state.isErrorEmail=falsereturn Promise.resolve();}}// 校验邮箱async function validatorEmailCode(_rule: Rule, value: string) {if (value!==state.remoteEmailCode) {return Promise.reject("验证码不正确!");}return Promise.resolve();}/*** 前往home!*/const jumpHome = () => {// 带 hash,结果是 /about#teamrouter.push({ path: "/" });};/*** 注册*/const registerAction = () => {if (formRef.value) {formRef.value.validate().then((res: any) => {state.spinning = true;const params = {username: state.formState.username,password: aes.encrypt(state.formState.password),emailCode:state.formState.code};registerUser(params).then((res: any) => {state.spinning = false;const { data: response } = res;console.log('response',response)if (response.code === 200) {store.commit("user/setUserToken", response.data);// 跳转setTimeout(()=>{jumpHome();},500)message.success(response.message);} else {message.warning(response.msg);}}).catch((r: any) => {state.spinning = false;message.error(JSON.stringify(r));throw Error(r);});});}};const delayTime=async ()=>{if (state.awaitTime>0) {await sleep(1)state.awaitTime-=1delayTime()}};const sendEmail=async ()=>{if(state.awaitTime>0){return}if(!state.formState.username){return message.warn('请输入邮箱!')}// if(state.isErrorEmail)// {//     return message.warn('请检查邮箱格式!')// }try{state.loadingEmailCode=trueconst res=await getEmailCode({email:state.formState.username})if(res?.data?.data?.emailRes?.code==200){// state.remoteEmailCode=res.data.data.codestate.isErrorEmail=false//  倒计时state.awaitTime=10message.success('发送邮件成功!请查收\t'+state.formState.username)delayTime()}else{state.isErrorEmail=falsemessage.warn(res.data.data.emailRes.msg.response)}}catch (e) {message.error(JSON.stringify(e))}finally {state.loadingEmailCode=false}}
</script><style lang="less">.background {/*background: #1890ff;  !* fallback for old browsers *!*//*background: -webkit-linear-gradient(to top, #000C40, #F0F2F0);  !* Chrome 10-25, Safari 5.1-6 *!*//*background: linear-gradient(to top, #000C40, #F0F2F0); !* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ *!*/}.container {/*background: #262626;*/background-clip: border-box;position: absolute;width: 100vw;height: 100vh;.background();}.register-container {position: absolute;min-width: 400px;left: 50%;top: 50%;transform: translate(-50%, -50%);box-sizing: border-box;}.submit-box {text-align: center;width: 100%;margin: 0 auto;}.register-container {background-color: rgba(255, 255, 255, 0.8);border-radius: 10px;padding: 0 20px;}.register-title {/*background: #1890ff;*//*color:#fff;*/width: 100%;text-align: center;font-weight: bolder;padding: 20px;font-size: 24px;}.description {margin-top: 20px;width: 100%;text-align: center;.description-after {color: #1890ff;cursor: pointer;}}
</style>

注册页面
register-page
注册发送邮件
sendEmailCode

⭐总结

前端vue3
a. 路由拦截
b. store缓存token

后端koa
a. jwt配置
b. jwt的白名单(发送验证码、注册、获取token)

Koa JWT is a middleware for Koa.js, a web application framework for Node.js. JWT, short for JSON Web Token, is a compact and self-contained mechanism for securely transmitting information between parties as a JSON object.
Koa JWT provides authentication and authorization functionalities using JWT in Koa applications. It allows you to generate and verify JWT tokens, protect routes from unauthorized access, and retrieve authenticated user information from the JWT token.

Koa JWT 是 Koa.js 的中间件,Koa 是 Node.js 的 Web 应用程序框架。JWT 是 JSON Web 令牌的缩写,是一种紧凑且独立的机制,用于将信息作为 JSON 对象在各方之间安全地传输。
Koa JWT 在 Koa 应用程序中使用 JWT 提供身份验证和授权功能。它允许您生成和验证 JWT 令牌,保护路由免受未经授权的访问,并从 JWT 令牌中检索经过身份验证的用户信息。

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!

shot

👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 最后,感谢你的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Promethues:普罗米修斯

promethues是一个开源的系统监控以及报警系统。整和zabbix的功能&#xff0c;系统&#xff0c;网络&#xff0c;设备promethues可以兼容网络&#xff0c;设备。容器监控。告警系统。因为他和k8s是一个项目基金开发的产品&#xff0c;天生匹配k8s的原生系统。对容器化和云原生服…

MySQL(下)

四、事务 一、概念 对数据库的一次执行中有多条sql语句执行。这多条sql在一次执行中&#xff0c;要么都成功执行&#xff0c;要么都不执行。保证了数据完整性。MySQL中只有innodb引擎支持事务。 二、特性 事务是必须满足 4 个条件&#xff08;ACID&#xff09;&#x…

江西公务员考试报名照如何上传成功

2024年江西省考报名公告已经发布了&#xff0c;想要参加江西省考报名的姐妹们&#xff0c;快来看看&#xff0c;提前了解报名流程&#xff0c;和报名照上传要求。 网上报名时间&#xff1a;1月28日9:00至2月2日17:00 网上缴费时间&#xff1a;1月28日9:00至2月3日17:00 打印准考…

备战蓝桥杯----数据结构及STL应用(基础2)

上次我们讲了vector的大致内容&#xff0c;接下来让我们讲一下栈&#xff0c;队列吧&#xff01; 什么是栈呢&#xff1f; 很简单&#xff0c;我们用的羽毛球桶就是&#xff0c;我们取的球&#xff0c;是最后放的&#xff0c;栈是一种先进后出的数据结构。 方法函数 s.push(…

SpringBoot深入解析:掌握自动装配机制及其定制化原理

推荐一款我一直在用的ChatGPT4.0国内站点&#xff0c;每日有免费使用额度&#xff0c;支持PC、APP、VScode插件同步使用 SpringBoot篇&#xff1a;SpringBoot的自动装配原理 SpringBoot是一个旨在简化Spring应用初始搭建以及开发过程的框架。它利用了Spring框架的依赖注入特性…

HDFS Federation前世今生

一 背景 熟悉大数据的人应该都知道&#xff0c;HDFS 是一个分布式文件系统&#xff0c;它是基于谷歌的GFS实现的开源系统&#xff0c;设计目的就是提供一个高度容错性和高吞吐量的海量数据存储解决方案。在经典的HDFS架构中有2个NameNode和多个DataNode&#xff0c;如下 从上面…

内网安全:RDP WinRS WinRM SPN Kerberos 横向移动

目录 WinRM协议 RDP协议 域横向移动&#xff1a;RDP协议 RDP协议利用 一. 探针服务 二. 获取NTML Hash 明文密码 三. 连接执行 域横向移动&#xff1a;WinRM WinRS WinRM协议、WinRS命令利用 一. cs 内置端口扫描5985 二. 连接执行 三. 上线CS 四. CS插件横向移动…

第五篇:express路由路径方式(字符串,字符串模式,和正则)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言&#xff1a; &#x1f4…

(二十一)Flask之上下文管理第二篇(细细扣一遍源码)

每篇前言&#xff1a; &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 &#x1f525;&#x1f525;本文已收录于Flask框架从入门到实战专栏&#xff1a;《Flask框架从入…

《幻兽帕鲁》1月29日游戏服务器推荐!腾讯云降低规格再次降价!

腾讯29日刷新规格&#xff0c;从14M降低到12M&#xff0c;硬盘和流量都有降低&#xff0c;但价格打下来了&#xff01;价格从66元/月降低到32元/月&#xff0c;277元/3个月降低到96元/3个月&#xff01; 三大厂商4核16G的云服务器价格对齐&#xff0c;不过具体参数略有不同 阿里…

C语言数据结构——链表

&#xff08;图像由AI生成&#xff09; 0.前言 在计算机科学中&#xff0c;数据结构是存储和组织数据的一种方式&#xff0c;它不仅影响数据的存储&#xff0c;也影响数据的检索和更新效率。C语言&#xff0c;作为一种经典的编程语言&#xff0c;提供了灵活的方式来处理数据…

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…

STM32学习笔记(二) —— 调试串口

我们在调试程序时&#xff0c;经常会使用串口打印相关的调试信息&#xff0c;但是单片机串口不能直接与 PC 端的 USB 接口通讯&#xff0c;需要用到一个USB转串口的芯片来充当翻译的角色。我们使用的开发板上有这个芯片&#xff0c;所以在打印调试信息的时候直接使用USB线连接开…

C#,广义斐波那契数(Generalised Fibonacci Numbers)的算法

广义斐波那契序列(generalized Fibonacci sequence)是斐波那契数的推广。由递推关系F₁F₂…Fm-10&#xff0c;Fₘ1&#xff0c;FmnFₙFn1…Fnm1&#xff0c;n≥1所产生的序列&#xff0c;称为m级广义斐波那契序列。 计算结果&#xff1a; 源代码&#xff1a; 1 文本格式 …

【MCAL】TC397+EB-tresos之GPT配置实战 - 定时器

本篇文章介绍了在TC397平台使用EB-tresos对GPT驱动模块进行配置的实战过程,不仅介绍了使用GTM来实现定时器的方案&#xff0c;还介绍了基于GPT12来实现连续定时器的实例。因为GTM是德国博世公司开发的IP&#xff0c;而英飞凌的芯片集成了这个IP&#xff0c;并在这个基础上搭建了…

蓝牙----蓝牙连接建立_连接建立

蓝牙----蓝牙连接建立_连接建立 蓝牙连接建立过程图1.主机扫描到广播包1.1判断是否是自己关心的广播包1.2广播地址添加到扫描列表 2.主机扫描结束&#xff0c;建立连接3.主从连接成功后&#xff0c;执行连接建立后事件3.1.主机将连接句柄和设备地址添加到连接列表3.2.主机进行G…

Docker 基础篇

目录 一、Docker 简介 1. Docker 2. Linux 容器 3. 传统虚拟机和容器的对比 4. Docker 的作用 5. Docker 的基本组成&#xff08;Docker 三要素&#xff09; 6. Docker 工作原理 7. Docker 架构 8. Docker 下载 二、Docker 安装 1. CentOS Docker 安装 2. CentOS8 …

贝锐蒲公英全新网页认证,保障企业访客无线网络安全

随着企业规模的不断扩大、人员的增长、无线终端数量/类型的增加&#xff0c;传统WiFi无线网络会暴露出越来越多的问题&#xff0c;导致无线网络管理困难。 比如&#xff1a;采用弱密码、安全防护不到位的默认设置、员工缺乏信息安全意识、未经授人员权访问无线网络…… 这些问…

SELINUX导致的网络服务问题解决

第一&#xff1a;开启相关服务&#xff0c;监控SELINUX 相关服务&#xff1a;setroubleshoot,auditd,大多数都是以se开头的 如果没有此服务&#xff0c;先yum下&#xff0c;然后查看状态 这里关于auditd说明&#xff0c;centos7不可以用systemctl重启auditd服务&#xff0c;…

深入了解Matplotlib中的子图创建方法

深入了解Matplotlib中的子图创建方法 一 add_axes( **kwargs):1.1 函数介绍1.2 示例一 创建第一张子图1.2 示例二 polar参数的运用1.3 示例三 创建多张子图 二 add_subplot(*args, **kwargs):2.1 函数介绍2.2 示例一 三 两种方法的区别3.1 参数形式3.2 布局灵活性3.3 适用场景3…