若依框架登录鉴权详解(动态路由)
若依框架登录鉴权:1.获取token(过期在响应拦截器中实现),2.基于RBAC模型获取用户、角色和权限信息(在路由前置守卫),3.根据用户权限动态生成(从字符串->组件,根据permission添加动态路由信息)和添加路由addRoutes(在路由前置守卫)
若依框架(Ruoyi)后端的登录权限身份认证流程是一个复杂但高效的过程,它确保了系统的安全性和数据的保护。以下是一个典型的若依框架后端登录权限身份认证流程,基于多个来源的信息进行归纳和整理:
1.发起请求获取认证凭证(token)
现象:用户未登录或者token过期,刷新页面将重定向至登录页面
- 如果用户身份验证通过,系统会生成一个认证凭证(如JWT,即JSON Web Token)。
- 认证凭证的生成过程可能包括生成一个唯一的UUID作为token的一部分,并设置token的有效期(这个有效期可能是通过Redis等缓存系统来控制的,因为JWT本身不直接支持自动刷新有效期)。
- 生成的token会包含用户的基本信息和权限信息,以便后续进行权限控制和身份验证。
- 后端通过验证后返回一个包含用户信息的令牌(Token),前端将这个令牌保存起来(如存储在Cookie、LocalStorage或SessionStorage中),以后的请求都需要带上这个令牌进行验证。
//login.vue
handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;if (this.loginForm.rememberMe) {Cookies.set("username", this.loginForm.username, { expires: 30 });Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });} else {Cookies.remove("username");Cookies.remove("password");Cookies.remove('rememberMe');}this.$store.dispatch("Login", this.loginForm).then(() => {//存储title至vuexthis.$store.commit("settings/SET_HEADTITLE",this.title)this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;if (this.captchaEnabled) {this.getCode();}});}});}
//store/modules/user.js
// 前端登录请求Login({ commit }, userInfo) {const username = userInfo.username.trim()const password = userInfo.passwordconst code = userInfo.codeconst uuid = userInfo.uuidreturn new Promise((resolve, reject) => {login(username, password, code, uuid).then(res => {//!!!key!!!//获取并设置后端传过来的tokensetToken(res.data.token)commit('SET_TOKEN', res.data.token)resolve()}).catch(error => {reject(error)})})},
//system/SysLoginController
//后端登录响应/*** 登录方法** @param loginBody 登录信息* @return 结果*/@SaIgnore@PostMapping("/login")public R<Map<String, Object>> login(@Validated @RequestBody LoginBody loginBody) {Map<String, Object> ajax = new HashMap<>();// 生成令牌String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),loginBody.getUuid());ajax.put(Constants.TOKEN, token);return R.ok(ajax);}
若依框架前端怎样判断token是否过期,并且要重新登录获取token:响应拦截器
在前端应用中,使用响应拦截器(response interceptor)来判断token是否过期,并在需要时重新登录以获取新的token是一种常见的做法。这通常涉及到以下几个步骤:
-
设置响应拦截器:在发起HTTP请求的库(如axios)中设置拦截器,以便在接收到响应时执行自定义逻辑。
-
检查响应状态码:在拦截器中检查响应的状态码,特别是那些表明需要身份验证的状态码(如401、403等)。
-
判断token是否过期:虽然401状态码通常表示未授权,但具体是否因为token过期导致需要额外的逻辑来判断。有时候,后端会在响应头中返回一个特定的字段(如
WWW-Authenticate
)来指示这一点,但更常见的是通过解析响应体中的信息(如错误信息)来判断。不过,在某些情况下,你可能只是简单地根据收到了401就认为token已过期。 -
重新登录获取token:如果确定token已过期,你可以尝试使用某种形式的“刷新token”来自动获取新的access token,或者引导用户重新登录。这取决于你的应用是否支持无感知重新登录(使用refresh token)以及后端的实现。
-
更新本地存储和重试请求:如果成功获取了新的token,更新本地存储中的token,并根据需要重试之前的请求。
-
错误处理:处理在重新登录过程中可能出现的任何错误,并向用户显示适当的反馈。
//utils/request.js
// 响应拦截器
service.interceptors.response.use(res => {// 未设置状态码则默认成功状态const code = res.data.code || 200;// 获取错误信息const msg = errorCode[code] || res.data.msg || errorCode['default']// 二进制数据则直接返回if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {return res.data}if (code === 401) {if (!isRelogin.show) {isRelogin.show = true;MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {isRelogin.show = false;store.dispatch('LogOut').then(() => {location.href = process.env.VUE_APP_CONTEXT_PATH + "index";})}).catch(() => {isRelogin.show = false;});}return Promise.reject('无效的会话,或者会话已过期,请重新登录。')}// else if (code === 500) {// Message({ message: msg, type: 'error' })//redis错误,待解决// return Promise.reject(new Error(msg))// }else if (code === 601) {Message({ message: msg, type: 'warning' })return Promise.reject('error')} else if (code !== 200) {Notification.error({ title: msg })return Promise.reject('error')} else {return res.data}},error => {console.log('err' + error)let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";} else if (message.includes("timeout")) {message = "系统接口请求超时";} else if (message.includes("Request failed with status code")) {message = "系统接口" + message.substr(message.length - 3) + "异常";}Message({ message: message, type: 'error', duration: 5 * 1000 })return Promise.reject(error)}
)
若报401错误表明token到期,401错误是一种常见的HTTP状态码,它表示“未授权”(Unauthorized)访问。当用户尝试访问受保护的资源时,如果未能提供有效的身份验证信息或提供的身份验证信息被拒绝,服务器就会返回401错误。这种错误通常出现在需要登录或授权才能访问的网站上,如银行网站、社交媒体网站等。
2. 获取认证信息(user、roles and permission)
不同用户的权限不同,右侧导航条【大层面】和页面中操作按钮【小层面】不同
- 将生成的token和用户的相关信息(如用户ID、角色、权限等)缓存起来,以便后续快速验证和查询。
//store/modules/user.js// 获取用户信息【前端请求】GetInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo().then(res => {const user = res.data.userconst avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : user.avatar;if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组commit('SET_ROLES', res.data.roles)commit('SET_PERMISSIONS', res.data.permissions)} else {commit('SET_ROLES', ['ROLE_DEFAULT'])}commit('SET_NAME', user.userName)commit('SET_AVATAR', avatar)resolve(res)}).catch(error => {reject(error)})})},//system/SysLoginController
//后端响应返回用户信息【user、roles and permisssions】/*** 获取用户信息** @return 用户信息*/@GetMapping("getInfo")public R<Map<String, Object>> getInfo() {LoginUser loginUser = LoginHelper.getLoginUser();SysUser user = userService.selectUserById(loginUser.getUserId());Map<String, Object> ajax = new HashMap<>();ajax.put("user", user);ajax.put("roles", loginUser.getRolePermission());ajax.put("permissions", loginUser.getMenuPermission());return R.ok(ajax);}
管理员
普通员工
根据权限动态生成并添加路由
步骤 1: 定义静态路由和异步路由
首先,你需要定义你的路由,通常分为静态路由(不需要权限即可访问的路由,如登录页、404页面等)和异步路由(需要权限的路由,如用户管理、订单管理等)。
/*** Note: 路由配置项** hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面* // 若你想不管路由下面的 children 声明的个数都显示你的根路由* // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数* roles: ['admin', 'common'] // 访问路由的角色权限* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限* meta : {noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svgbreadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。}*/
// router/index.js import Vue from 'vue'
import Router from 'vue-router' Vue.use(Router) // 静态路由
const constantRoutes = [ { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }
] // 异步路由(示例)
const asyncRoutes = [ { path: '/user', component: () => import('@/views/user/index'), meta: { title: '用户管理', roles: ['admin', 'editor'] } }, // 更多路由...
] const createRouter = () => new Router({ // mode: 'history', // 需要后端支持 scrollBehavior: () => ({ y: 0 }), routes: constantRoutes
}) const router = createRouter() // 动态添加路由
function addAsyncRoutes(routes) { routes.forEach(route => { router.addRoute(route) })
} export { router, addAsyncRoutes, asyncRoutes
}
步骤 2: 路由守卫与权限验证和路由添加
在用户登录后,你需要从后端获取用户的权限信息,并根据这些权限信息来过滤和添加路由。
你还需要设置路由守卫来确保用户在没有权限时不能访问某些页面。
//src/permission.js
//前端路由守卫
router.beforeEach((to, from, next) => {NProgress.start()if (getToken()) {//获取路由的mata.title属性,并存储在Vuex中to.meta.title && store.dispatch('settings/setTitle', to.meta.title)/* has token*/if (to.path === '/login') {next({ path: '/' })NProgress.done()} else {if (store.getters.roles.length === 0) {isRelogin.show = true// 判断当前用户是否已拉取完user_info信息store.dispatch('GetInfo').then(() => {isRelogin.show = false//生成路由store.dispatch('GenerateRoutes').then(accessRoutes => {// 根据roles权限生成可访问的路由表//添加路由router.addRoutes(accessRoutes) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成})}).catch(err => {store.dispatch('LogOut').then(() => {Message.error(err)next({ path: '/' })})})} else {next()}}} else {// 没有tokenif (whiteList.indexOf(to.path) !== -1) {// 在免登录白名单,直接进入next()} else {next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页NProgress.done()}}
})
获取与添加**路由**
//生成路由store.dispatch('GenerateRoutes').then(accessRoutes => {// 根据roles权限生成可访问的路由表//添加路由router.addRoutes(accessRoutes) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成})
GenerateRoutes
请求得到的sdata与rdata
//store/module/permisssion.js// 生成路由GenerateRoutes({ commit }) {return new Promise(resolve => {// 向后端请求路由数据getRouters().then(res => {const sdata = JSON.parse(JSON.stringify(res.data))const rdata = JSON.parse(JSON.stringify(res.data))//console.log(sdata,rdata); //arrayconst sidebarRoutes = filterAsyncRouter(sdata)const rewriteRoutes = filterAsyncRouter(rdata, false, true)// 动态路由,基于用户权限动态去加载const asyncRoutes = filterDynamicRoutes(dynamicRoutes);rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })router.addRoutes(asyncRoutes);commit('SET_ROUTES', rewriteRoutes)commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))commit('SET_DEFAULT_ROUTES', sidebarRoutes)commit('SET_TOPBAR_ROUTES', sidebarRoutes)resolve(rewriteRoutes)})})}// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {return asyncRouterMap.filter(route => {//type标识sidebarRoutes(false)与rewriteRoutes(true)if (type && route.children) {route.children = filterChildren(route.children)}if (route.component) {// Layout ParentView 组件特殊处理if (route.component === 'Layout') {route.component = Layout} else if (route.component === 'ParentView') {route.component = ParentView} else if (route.component === 'InnerLink') {route.component = InnerLink} else {//指定目录下找对应组件route.component = loadView(route.component)}}if (route.children != null && route.children && route.children.length) {//递归route.children = filterAsyncRouter(route.children, route, type)} else {delete route['children']delete route['redirect']}return true})
}function filterChildren(childrenMap, lastRouter = false) {var children = []childrenMap.forEach((el, index) => {if (el.children && el.children.length) {if (el.component === 'ParentView' && !lastRouter) {el.children.forEach(c => {c.path = el.path + '/' + c.pathif (c.children && c.children.length) {children = children.concat(filterChildren(c.children, c))return}children.push(c)})return}}if (lastRouter) {el.path = lastRouter.path + '/' + el.path}children = children.concat(el)})return children
}export const loadView = (view) => {if (process.env.NODE_ENV === 'development') {return (resolve) => require([`@/views/${view}`], resolve)} else {// 使用 import 实现生产环境的路由懒加载return () => import(`@/views/${view}`)}
}
//权限判断
//store/modules/permission.js// 动态路由遍历,验证是否具备权限export function filterDynamicRoutes(routes) {const res = []routes.forEach(route => {if (route.permissions) {if (auth.hasPermiOr(route.permissions)) {res.push(route)}} else if (route.roles) {if (auth.hasRoleOr(route.roles)) {res.push(route)}}})return res
}//plugins/auth.jsimport store from '@/store'function authPermission(permission) {const all_permission = "*:*:*";const permissions = store.getters && store.getters.permissionsif (permission && permission.length > 0) {return permissions.some(v => {return all_permission === v || v === permission})} else {return false}
}function authRole(role) {const super_admin = "admin";const roles = store.getters && store.getters.rolesif (role && role.length > 0) {return roles.some(v => {return super_admin === v || v === role})} else {return false}
}export default {// 验证用户是否具备某权限hasPermi(permission) {return authPermission(permission);},// 验证用户是否含有指定权限,只需包含其中一个hasPermiOr(permissions) {return permissions.some(item => {return authPermission(item)})},// 验证用户是否含有指定权限,必须全部拥有hasPermiAnd(permissions) {return permissions.every(item => {return authPermission(item)})},// 验证用户是否具备某角色hasRole(role) {return authRole(role);},// 验证用户是否含有指定角色,只需包含其中一个hasRoleOr(roles) {return roles.some(item => {return authRole(item)})},// 验证用户是否含有指定角色,必须全部拥有hasRoleAnd(roles) {return roles.every(item => {return authRole(item)})}
}
addRoute
router.addRoutes(accessRoutes) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
第一行:router.addRoutes(accessRoutes)
这行代码的作用是动态地向Vue Router的路由表中添加路由。addRoutes
方法是Vue Router早期版本(Vue Router 2.x)中用于动态添加路由的方法。然而,需要注意的是,在Vue Router 3.x及更高版本中,官方推荐使用addRoutes
的替代方案,即通过router.matcher.addRoutes
或者完全重新创建router实例来动态添加路由(因为addRoutes
方法在Vue Router 4.x中被移除)。但这里我们仍然以addRoutes
为例进行说明。
router
:这是Vue Router的实例,它包含了应用的所有路由配置。addRoutes
:这是Vue Router实例上的一个方法,用于向路由表中添加新的路由规则。accessRoutes
:这是一个数组,包含了要添加到路由表中的路由对象。每个路由对象都遵循Vue Router的路由配置规范,包括path
、component
、children
等属性。
第二行:next({ ...to, replace: true })
这行代码通常出现在Vue Router的导航守卫(Navigation Guards)中,用于控制路由的跳转。next
函数是导航守卫的回调函数,它决定了路由跳转的行为。
next
:这是一个必须调用的函数,用于解决守卫中的钩子。调用它时,可以传入一个位置对象或者一个错误。如果不调用,则整个路由导航都将被“挂起”。{ ...to, replace: true }
:这里使用了对象展开语法(...
)来复制to
对象的所有属性到一个新对象中,并添加或修改replace
属性为true
。to
对象通常包含了即将要跳转到的路由信息,如path
、query
等。replace: true
:这个选项的作用是,当使用next
函数进行路由跳转时,不是将新路由添加到历史记录堆栈中,而是替换掉当前的路由。这通常用于避免在路由跳转后留下无法退回到当前路由的“死胡同”。
结合使用这两行代码的场景
在实际应用中,这两行代码经常一起使用在基于用户权限的路由控制中。比如,当用户登录后,后端返回了用户的权限信息,前端根据这些权限信息动态生成可访问的路由表(accessRoutes
),并通过router.addRoutes(accessRoutes)
将这些路由添加到路由表中。然后,在导航守卫中,使用next({ ...to, replace: true })
来确保用户被重定向到目标路由,并且由于replace: true
,这个跳转不会在历史记录中留下当前路由的入口,从而避免了用户通过浏览器的前进/后退按钮访问到未授权的路由。
使用路由守卫保证addRoute执行完成:你可以设置一个全局前置守卫(beforeEach)或后置守卫(afterEach),并在其中检查路由是否已经存在于路由表中。然而,这种方法比较复杂且可能不够直观,因为它依赖于路由守卫的多次调用和状态检查。
根据权限限制页面的操作按钮(新增、修改和删除操作等)
相关文章:

若依框架登录鉴权详解(动态路由)
若依框架登录鉴权:1.获取token(过期在响应拦截器中实现),2.基于RBAC模型获取用户、角色和权限信息(在路由前置守卫),3.根据用户权限动态生成(从字符串->组件,根据permission添加动…...
孤儿进程、僵尸进程、守护进程(精灵进程)
目录 一、孤儿进程 二、僵尸进程 三、守护进程(精灵进程) 一、孤儿进程 定义:孤儿进程是指那些其父进程已经结束,但它们依然在运行的进程 创建一个孤儿进程: #include <stdio.h> #include <stdlib.h> #in…...
Centos9 网卡配置文件
1、Centos stream 9 网络介结 Centos以前版本,NetworkManage以ifcfg格式存储网络配置文件在/etc/sysconfig/networkscripts/目录中。但是,Centos steam 9现已弃用ifcfg格式,默认情况下,NetworkManage不再创建此格式的新配置文件。…...

ios免签H5
1、windows下载mobileconfig文件制作工具,可在csdn搜索iPhone_Mobileconfig_Tool下载安装;IOS 从APP Store 下载Apple Configurator 2 2、用申请的域名SSL证书给mobieconfig文件签名,最好下载Apache证书,里面包含 AE86211.crt…...
RedHat9.x-基本操作
本例以RedHat9.3为例 安装ifconfig命令 # dnf -y install net-tools 安装图形化 # dnf -y groupinstall "Server with GUI" 安装tmux # dnf -y install tmux -- screen已弃用 network-scripts 已弃用...

华为 HCIP-Datacom H12-821 题库 (5)
有需要题库的可以看主页置顶 需要题库的加Q裙 V群仅进行学习交流 1.以下关于堆叠 MAD 检测说法错误的是? A、堆系统互为代理进行 MAD 检测时,两个堆系统可以使用相同的D omain ID B、MAD 检测的方式分为直连检测、代理检测 C、MAD 代理检测要求所有堆叠…...
vue中oninput和@input区别
使用oninput绑定,此方法会导致中文输入法情况下,v-model和value不一致,原因是在中文输入法情况下vue中的v-model会自动return,v-mode绑定的值不变,value绑定的值可变。 :οninput"valuevalue.replace(/\D/g,)&qu…...
分布式锁(Redis的setnx、Redisson)
一、使用Redis的setnx实现分布式锁 1、使用Redis的setnx实现分布式锁出现的问题 (1) 宕机时的锁释放问题 在分布式系统中,如果一个节点获取了锁,但在执行任务过程中发生故障,没有释放锁,其他节点可能会一…...

从0开始深度学习(4)——线性回归概念
1 线性回归 回归(regression)指能为一个或多个自变量与因变量之间的关系进行建模。 1.1 线性模型 线性假设是指目标可以表示为特征的加权和,以房价和面积、房龄为例,可以有下面的式子: w称为权重(weigh…...

C语言中的预处理指令中的其中一对——#ifdef和#ifndef
目录 开头1.什么是#ifdef和#ifndef?2.#ifdef和#ifndef的实际应用判断ABCD这个宏是否被定义过判断HELLO这个宏是否没被定义过防止头文件重复定义 下一篇博客要说的东西 开头 大家好,我叫这是我58。今天,我们要学一下关于C语言中的预处理指令中的其中一对…...

交换机自动化备份配置(H3C_无人值守)
介绍: 在日常运维过程中,需要定时备份设备的配置,在设备数量过于庞大的情况下,对我们的运维工作会造成极大地不便,通过python自动化能够完美解决人工手动保存设备配置的问题。而且自动化运维在未来也一定是大势所趋&a…...
缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案? 在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。 那么怎么实现缓存预热呢? 一般来说,我…...

「iOS学习」——Masonry学习
iOS学习 前言Masonry的属性Masonry的使用基础APIAuto Boxing修饰语倍数中心点设置边距优先级使用 总结 前言 暑假我们学习了使用CocoaPods引入第三方库,实现使用SVG图片。而Masonry作为一个轻量级的布局架构,在使用中可以节省很多时间。故进行简单学习。…...

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署GitLab服务器
828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署Gitlab服务器 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、GitLab介绍2.1 GitLab简介2.2 GitLab主要特点 三、本次…...

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】
1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息; 2、DHT11采集病房温湿度信息&…...

计算机毕业设计 | SpringBoot+vue 游戏商城 steam网站管理系统(附源码)
1,项目背景 国家大力推进信息化建设的大背景下,城市网络基础设施和信息化应用水平得到了极大的提高和提高。特别是在经济发达的沿海地区,商业和服务业也比较发达,公众接受新事物的能力和消费水平也比较高。开展商贸流通产业的信息…...
【CH395的简单示例代码】
提供一个基于CH395的简单示例代码,这里将展示如何初始化CH395,并发送一个简单的HTTP请求。请注意,实际使用时还需要根据具体的硬件平台和开发环境调整代码。 假设我们使用的是一个具有SPI接口的微控制器,并且已经将CH395连接到该…...
AI模型:追求全能还是专精?
目录 引言 一、全能型AI模型的诱惑 1.1 通用智能的愿景 1.2 资源整合的优势 1.3 应对未知挑战的能力 1.4 挑战与不足 二、专精型AI模型的魅力 2.1 深度与精度的提升 2.2 成本控制与效率优化 2.3 易于监管与解释性增强 2.4 挑战与不足 三、全能型与专精型AI的全面评…...

ffmpeg音视频开发从入门到精通——ffmpeg 视频数据抽取
文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结 环境…...
Node.js之文件夹的操作
1.创建文件夹操作 // 导入fs模块 const fs require(fs)//创建文件夹操操作 fs.mkdir(./Page, err > {if (err) {console.log(操作失败)return}console.log(操作成功) }) 2.递归创建文件夹 //递归创建文件夹 fs.mkdir(./a/b/c, {recursive: true}, err > {if (err) {co…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...