基于 vuestic-ui 实战教程 - 登录篇

1. 简介

登录做为一个系统的门面,也是阻挡外界的一道防线,那在vuestic-ui中如何做登录功能呢。在这里就之间沿用初始版本的Login页面,作为一个演示模板,后续需要改进的读者可以在此篇文章的基础上修改。

在这里插入图片描述

2. 登录接口相关api 与 type编写

在上一篇获取动态数据中 我们已经定义好了与ts整合的axios,实现发送异步请求与远程服务器交互(对于ts语法像是函数定义、基本数据类型还是有不懂的读者可以跳转到上一篇的2.1再学习学习)这里就直接引入登录接口的api编写, 具体位置如下我个人习惯创建一个api文件夹,里面专门存放一些与后端交互的api方法和类型定义(初始quickstart版本是写在上面的page中的,就看个人的编写习惯吧😁只要功能实现了就没问题)

在这里插入图片描述

对于index.ts中主要实现了三个基本的方法,登录登出和获取用户信息

这里没有实现注册功能,因为我实现该网站主要是做一个流量监控的系统,注册功能对于用户不多的情况下其实不太需要,管理员可以直接操作加入数据库中,要是对这快感兴趣的读者也可以自己尝试尝试注册模块的功能实现
本质就是add一个user到数据库中,不过需要注意的是添加验证码等防护措施,防止有不法分子大量注册短时间内打爆服务器!!

import { http } from '../../../utils/request'
import type { LoginData , UserInfoRes} from './types'const requestContent = '/simple/cloud/access'
/*** 登录*/
export function login(loginVo: LoginData) {return http.post<UserInfoRes>(`${requestContent}/login`, loginVo);
}/*** 获取登录用户信息*/
export function getUserInfo() {return http.post<UserInfoRes>(`${requestContent}/info`)
}  /*** 退出登录*/
export function logout() {return http.post<string>(`${requestContent}/logout`)
}  

假设访问的后端服务器使用URL - http:localhost:9001/simple/cloud/access/login 这里由于uri前缀是一样的都是 ‘/simple/cloud/access’ 所以把它提取出来做为一个常量简化编写。只需要在使用的地方通过变量占位符引入就好啦(注意不是 ‘’ ,刚开始也踩过这个坑在vscode中看到上面的requestContent 由灰色变成高亮则说明引用成功)

`${key}`

而对于api中引入的数据类型定义在types.ts中,其中的返回值类型就根据后端提供的接口方法来写,像是我后端返回的类型为一个Map<String,Object> 类型的对象如下图所示,那我就根据这个map中的key和value一一对应写出如下的接口UserInfoRes,然后使用export导出给外部使用
在这里插入图片描述

注意编写的过程中只用指定ts基本的类型(java的List对应的就是ts中的数组 - 使用 [ ] 进行初始化 ),而要是需要返回一个User类型的对象,那就需要重新定义一个UserInfoInterface ,或者在user对应的api处定义types.ts 再在该文件下在引入(我是更推荐这种做法👍

/* 登录接口参数类型 */
export interface LoginData {email: string,password: string,
}/* 用户信息接口返回值类型 */
export interface UserInfoRes {routers: [],buttons: [],roles: [],name: string,token: string,
}

3. 修改Login.vue

定义好与后端交互的方法api后,我们就可以回到前面的Login.vue处修改具体登录逻辑啦,由于初始版本使用的全是静态数据,所以很多功能其实都是不用的,具体删除修改后的模板如下(只保留了一个忘记密码的选项,该功能后续再完善😭先把主要的逻辑跑通先,感兴趣的读者可以先占个坑,后续我一定会回来填坑的!)

<template><VaForm ref="form" @submit.prevent="submit"><h1 class="font-semibold text-4xl mb-4">Log in</h1><VaInputv-model="formData.email":rules="[validators.required, validators.email]"class="mb-4"label="Email"type="email"/><VaValue v-slot="isPasswordVisible" :default-value="false"><VaInputv-model="formData.password":rules="[validators.required]":type="isPasswordVisible.value ? 'text' : 'password'"class="mb-4"label="Password"@clickAppendInner.stop="isPasswordVisible.value = !isPasswordVisible.value"><template #appendInner><VaIcon:name="isPasswordVisible.value ? 'mso-visibility_off' : 'mso-visibility'"class="cursor-pointer"color="secondary"/></template></VaInput></VaValue><div class="auth-layout__options flex flex-col sm:flex-row items-start sm:items-center justify-between"><RouterLink :to="{ name: 'recover-password' }" class="mt-2 sm:mt-0 sm:ml-1 font-semibold text-primary">Forgot password?</RouterLink></div><div class="flex justify-center mt-4"><VaButton class="w-full" @click="submit"> Login</VaButton></div></VaForm>
</template>

重写绑定的submit点击事件逻辑

const submit = () => {if (validate()) {login(formData).then((data: UserInfoRes) => {if (data) {// 在这里添加需要执行的操作const token = data.token;// 将token存储到authStore中const authStore = useAuthStore()authStore.setToken(token)authStore.setIsAuthenticated(true)window.sessionStorage.setItem('isAuthenticated', 'true')authStore.setName(data.name)authStore.setButtons(data.buttons)authStore.setRoles(data.roles)authStore.setRouters(data.routers)init({ message: "logged in success", color: 'success' });// 登陆成功后就重定向到主页面dashboardpush({ name: 'dashboard' })}}).catch(() => {init({ message: "logged in fail , please check carefully!", color: '#FF0000' });});}else{Message.error('error submit!!')return false}
}

看到这里我相信你肯定会疑惑,为什么我需要获取到数据又存储到store中,那这个store又在哪里定义的呢,作者也没讲啊😡
别急别急,请听我细细道来

4. store实现

在Vue应用程序中,当需要管理共享状态时,通常会使用Vuex库,而store就是Vuex中用于存储这些状态的地方,而我们登录后自然需要围护当前登录角色的一些关键信息(权限,姓名等等)需要的时候就直接到store中拿去,而不是反复的去数据库中查找,废话不多说下面就来定义一个store ,在初始版本中就已经定义好了store,只不过这个store里面是没东西的,如下图所示

在这里插入图片描述

那我们就可以在原有的基础上添加修改,下面的代码都是在index.ts中实现的,如下代码就是一个模板,对应pinia库的描述如下
Pinia是Vue的另一种状态管理方案,与Vuex类似,但设计上更简洁、更易于上手。以下是关于Pinia的一些详细说明:

  • 简单易用:Pinia的目标是提供一个更简单的状态管理解决方案,它的API设计非常直观,使得开发者可以快速上手并有效地管理状态。
  • 独立模块:与Vuex不同,Pinia中的每个store都是一个独立的模块,它们可以单独导入和导出,这有助于更好地组织和维护代码。
  • 响应式:Pinia中的状态是响应式的,当状态发生变化时,依赖于这些状态的组件会自动更新。
  • Devtools支持:Pinia具有良好的Devtools支持,可以帮助开发者更方便地跟踪和调试状态变化。
  • 插件化:Pinia被设计为一个插件,可以轻松地集成到现有的Vue应用中。
  • 与Vuex兼容:虽然Pinia是一个全新的状态管理库,但它也允许与Vuex共存于同一个项目中,方便开发者逐步迁移。

本次项目中store就基于Pinia实现,首先通过defineStore方法定义一个全局可供调用的store, 其中包括了一些属性像是

  1. id (自己设定,但是要保证全局唯一)
  2. state (定义的所有状态)
  3. getters (获取状态的方法)
  4. actions (有获取肯定就有设置的方法啦)
// store.ts
import { createPinia, defineStore } from 'pinia'export const useAuthStore = defineStore({id: 'auth',state: () => ({}),getters: {},actions: {},
})export default createPinia()

4.1 state

在state中定义的状态就是在一个浏览器会话内需要存储的用户信息(登录后赋值,登出或者会话结束就销毁)根据第2点中types定义的UserInfoRes 可以设计出来, 由于ts不像js一样是弱语言,ts是有类型的上一讲也提到过,所以为了能在后续的get set中拿到指定和设置其中的属性值,我们需要通过as 参数类型的方式来指定

isAuthenticated 本意是为了阻止用户登录前就访问其他的页面(会被驳回,重定向到登录页面)后面发现存到浏览器缓存中也是可以的,这里就做个备选,看读者喜欢哪一种方式

state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),

这里的RoleDta和RouterVo就分别对应了角色和菜单列表,具体实现如下(编写在types.ts文件中,具体位置看下边4.4的总体代码)


/* sysUser参数类型 */
export interface RoleData {id: number,roleName: string,roleCode: string,description: string
}/* RouterVo参数类型 */
export interface RouterVo {path: string,hidden: boolean,alwaysShow: boolean,meta: MetaVo,children: RouterVo[],
}

4.2 getters

根据如下的指定格式获取存在store中的参数

getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,
},

4.3 actions

actions中定义了一系列set方法,可以发现这里()内的参数都是指定类型的,如果我们在定义的时候不指定类型这就会报错!!

actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},// 登出后的资源重置reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},
},

4.4 总体代码

// store.ts
import { createPinia, defineStore } from 'pinia'
import { RoleData } from '@/api/system/sysRole/types'
import { RouterVo } from '@/api/system/sysMenu/types'export const useAuthStore = defineStore({id: 'auth',state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,},actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},},
})
// 记得要导出,不在就白定义了 外部通过调用createPinia() 获取示例
export default createPinia()

4.5 main.ts中App引入

在Vue中引入App是因为App.vue通常作为项目的主组件和页面入口文件,负责构建定义及页面组件的归集和切换。定义的组件自然要添加到其中,在初始化的时候就一同创建。在文件原有基础上添加如下代码

import stores from './stores'
import { createPinia } from 'pinia'app.use(createPinia)
app.use(stores)

最后保存就好啦,到这里在回看第3点的submit方法是不是就一目了然
这里提炼出使用store的核心代码,有需要的读者可以直接复制使用😁

// 导入刚刚定义的方法
import { useAuthStore } from '@/stores'// 外部调用创建一个示例(唯一的)
const authStore = useAuthStore()
// 在对应的操作方法里面使用我们在getters和actions中定义的方法
// set
authStore.setToken(token)
// get
const token = authStore.getToken

5. vue限制实现不登录无法进入其他页面

这个模块可用的方法有很多网上也是有各种各样的教程,在这里使用的是设置路由守卫的方法,在router/index.ts下修改,具体做三种判断

  1. 防止重复登录: 登录后的用户不能在登录了,只能主动退出或者关闭浏览器(token失效也是一个,这个后面讲)
  2. 白名单直接放行:对于可以供给全部用户访问的一些静态资源、页面(比如登录页面,和一些docs帮助文档是可以直接访问的)
  3. 没有登录:对于没有登录的用户无法访问系统的资源,为了提防有些通过导航栏修改URL的方法访问
// 设置哪些页面是属于白名单的
const witheList = ["/auth/login"];function isWitheRoute(path : string) {return witheList.includes(path);
}// 全局前置守卫
router.beforeEach((to, from, next) => {const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//防止重复登录if (isAuthenticated && (to.path === "/auth/login"))  {Message.info("You have successfully logged in. Please avoid logging in repeatedly! (You can log out if you wish)");return next({ path: from.path ? from.path : "/" });}// 判断如果是白名单就直接放行if (isWitheRoute(to.path)) {next();return;}// 没有登录,强制跳转到登录页面if (!isAuthenticated && to.path != "/auth/login") {Message.info("Please logging first");next({ path: "/auth/login" });return;}  next()
});

5.1. 浏览器缓存

上边埋了一个坑,可以使用浏览器缓存的方法实现该功能,上边代码也看到了window.sessionStorage. 那么这到底是嘛玩意,作用范围生命周期又是什么呢?下面将一一解答:

  1. sessionStorage为Web开发者提供了一种在用户的浏览器中临时存储数据的方式。这种存储方式特定于用户打开的特定窗口或标签页,并且数据只在这个特定的窗口或标签页有效。当用户关闭这个窗口或标签页时,存储在sessionStorage中的所有数据将被清除。这就意味着不同的浏览器窗口或标签页,即使是打开相同的网页,它们之间的sessionStorage数据是不共享的。
  2. sessionStorage的生命周期与用户打开的窗口或标签页的持续时间同步。只要窗口或标签页保持打开状态,即便是进行页面刷新或切换到同源的其他页面,sessionStorage中的数据都将持续存在。然而,一旦窗口或标签页被关闭,sessionStorage中的所有数据将立即失效并被清除。

可以见得通过该方法保存用户的登录状态也是不错之选,而且非正常退出时候也不用担心数据泄露(会自动销毁,后端的数据就需要通过勾子函数回调,或者直接设置redis过期时间就等它自动过期)下边就是三个常用的方法:

对于我们的登录功能来说,在登录成功后设置为true,此时路由守卫判断时候就能获取到该值,而在登出的时候就删除掉该数据,这样就能保证统一

//设置对应的key-value
window.sessionStorage.setItem('isAuthenticated', 'true');//通过getItem获取 (取不到时为null)
const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//去除浏览器缓存
window.sessionStorage.removeItem('isAuthenticated')

6. 登出功能实现

登出功能本质上是跟登录没什么区别的,就是后端清除存储的数据token , reids中权限数据等,前端清除login获取到的所有数据(回到出厂设置的感觉)在初始版本中是没有登出这个按钮的,经常登录网页的朋友都知道,登出的按钮一般是在右上角,那这里我们就遵循惯例先找找最上边的栏目是在哪一个vue页面里面(最笨的方法就是一个一个去搜索是否有相应的字眼)

那么我就以我的理解来告诉大家如何快速找到相应的模块。首先要知道的是所有的组件都是放在src/components文件夹下的,那我们就去下边找,一展开就很明显看到navbar的字眼(导航栏嘛,也就是我们要找的上边栏所在位置)点开后发现又有个components(根据上面的知识不用我说都知道这是放组件的吧)点开就看到GitHubButton 这不就是我们要找的上边栏上的github按钮吗,说明我们找对地方了,最终就锁定范围在这两个vue文件中,是不是一下子节省很多工作量😁 , 具体示例文件所在处如下图所示
在这里插入图片描述

找到这个文件后我们预期的效果是跟下图这样加一个Logout 按钮 用户点击就可以退出登录
在这里插入图片描述
在AppNavbarActions这个文件中点开就发现其实实现起来很简单,就是依葫芦画瓢,照抄原来有的button组件就好啦,具体代码如下

<VaButtonv-if="!isMobile"preset="secondary"@click="logoutOper" <!--自定义点击事件-->target="_blank"color="textPrimary"class="app-navbar-actions__item flex-shrink-0 mx-0"
>{{ t('Logout') }}
</VaButton>

因为我们绑定了点击事件,自然要实现的啦(如下代码在script中原有的基础上添加)

import { logout } from '@/api/system/auth/index'
import { useAuthStore } from '@/stores'
import { useToast } from 'vuestic-ui'
import { useRouter } from 'vue-router'
const { push } = useRouter()
const { init } = useToast()const logoutOper = () => {logout().then(() => {const store = useAuthStore() // 获取store实例store.reset() // 重置store//去除浏览器缓存window.sessionStorage.removeItem('isAuthenticated')//跳转路由init({ message: "logout success", color: 'success' });push({ name: 'login' })}).catch(() => {init({ message: "logged out fail , please contact administration", color: '#FF0000' });});
}

7. 每次请求时带上token访问服务器

由于加入了权限认证功能,所以登录后的每一次请求都必须携带上token(这里的token就遵循OAuth2的规范以"Bearer "开头),不然会认为没有登录跳转的登录页面重新登录,在每一次请求中添加请求头是不是就是定义一个全局filter,也就是在上一讲中提到的axios请求拦截器,那如下代码就在utils/request.ts下修改(还没有的请看上一讲)

import { useAuthStore } from '@/stores'/* 请求拦截器 */
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {  const authStore = useAuthStore()if (authStore != undefined) {//获取tokenconst token = authStore.getTokenconfig.headers.Authorization = `Bearer ${token}`;} else {// 如果不存在 token,则拒绝请求并跳转到登录页面window.location.href = '/auth/login';//去除浏览器缓存window.sessionStorage.removeItem('isAuthenticated')return Promise.reject('Authenticated fail');}return config;  }, (error: AxiosError) => {Message.error(error.message);return Promise.reject(error)
})

终于讲完啦,这篇内容挺多的,给看到这里的读者点赞👍,希望能够对你们有所帮助(本篇主要实现前端的功能,后续会结合权限管理给出后端认证授权功能实现,敬请期待…)


各位读者我回来填坑啦,对于上面的后端实现我又写了点自己的想法,感兴趣的读者可以点击查阅后端认证授权功能实现

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

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

相关文章

python连接mysql,并整理(去哪儿网)页面数据到表

##引入requests/pymysql模块 本地安装mysql数据库&#xff0c;安装图形化工具navicat import requests from pymysql import Connect#创建客户端连接信息 client Connect(host127.0.0.1,port3306,userroot,password, ) #创建游标 cursor client.cursor() cursor.execute(cre…

17- PHP 开发-个人博客项目TP 框架路由访问安全写法历史漏 洞

常见的php框架&#xff1a;laravel和thinkphp和yii 这里以thinkphp为例 thinkphp目录访问设置 这里只找到了这个3.多的源代码&#xff0c;没找点5.的&#xff0c;凑合一下 链接&#xff1a;GitHub - top-think/thinkphp: ThinkPHP3.2 ——基于PHP5的简单快速的面向对象的PHP…

HTML用法介绍

文章目录 一、HTML概念和模版二、常用标签及用法1.p标签2.span标签3.h标签4.hr标签5.img标签6.a标签7.input标签8.table标签 一、HTML概念和模版 HTML的全称为超文本标记语言&#xff0c;它包括一系列标签组成&#xff0c;模版及各部分注释如下&#xff1a; <!--声明文档类…

ROS基础学习-话题通信机制研究

研究ROS通信机制 研究ROS通信机制 0.前言1.话题通信1.1 理论模型1.2 话题通讯的基本操作1.2.1 C++1.2.2 Python中使用自己的虚拟环境包1.2.2.1 参考11.2.2.2 参考21.2.2.3 /usr/bin/env:“python”:没有那个文件或目录1.2.3 Python1.2.2.1 发布方1.2.2.2 订阅方1.2.2.3 添加可执…

《征服数据结构》目录

我们知道要想学好算法&#xff0c;必须熟练掌握数据结构&#xff0c;数据结构常见的有 8 大类&#xff0c;分别是数组&#xff0c;链表&#xff0c;队列&#xff0c;栈&#xff0c;散列表&#xff0c;树&#xff0c;堆&#xff0c;图。但如果细分的话就比较多了&#xff0c;比如…

go-zero 实战(2)

go-zero 实战&#xff08;1&#xff09; 中&#xff0c;使用了go-zero 创建了order 和 user 两个微服务。而order作为grpc的客户端&#xff0c;user 作为grpc的服务端&#xff0c;打通了 order 到 user的调用。接下来&#xff0c;我们在user中&#xff0c;加入mysql组件。确保数…

我说同事咋找工作命中率这么高,原来是学习了这些招式

最近有两个同事离职了&#xff0c;其中一个还是专科&#xff0c;他俩一个是前端开发&#xff0c;一个是python开发&#xff0c;两个人都接近35岁了。我们还劝告他们&#xff0c;不要离职&#xff0c;要骑驴找马。但了解后&#xff0c;他俩非常有信心的说&#xff1a;不怕&#…

3D视觉技术|螺栓分拣测试

随着制造业自动化程度的不断提高&#xff0c;某大型汽配企业为提升生产效率、减少人力成本&#xff0c;提出了使用复合机器人完成螺栓分拣的需求。富唯智能通过采用复合机器人&#xff0c;结合3D工业相机和高性能控制器&#xff0c;实现螺栓的自动抓取&#xff0c;从而提升生产…

鸿蒙OS开发:【一次开发,多端部署】(一多天气)项目

一多天气 介绍 本示例展示一个天气应用界面&#xff0c;包括首页、城市管理、添加城市、更新时间弹窗&#xff0c;体现一次开发&#xff0c;多端部署的能力。 1.本示例参考一次开发&#xff0c;多端部署的指导&#xff0c;主要使用响应式布局的栅格断点系统实现在不同尺寸窗…

【Qt 学习笔记】Qt窗口 | 工具栏 | QToolBar的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 工具栏 | QToolBar的使用及说明 文章编号&#xff1a;Qt 学习…

opencv文档py_contours示例整理

文章目录 目录说明contours_begin目标什么是轮廓?如何画等高线?轮廓逼近法contour_features目标1.Moments 时刻2. Contour Area 轮廓面积3. Contour Perimeter 轮廓周长4. Contour Approximation 轮廓近似5. Convex Hull 凸包6. Checking Convexity 检查凸性7. Bounding Rect…

嵌入式进阶——RTC时钟

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 RTC时钟原理图PCF8563寄存器控制与状态寄存器 设备地址I2C环境初始化RTC寄存器数据读取RTC寄存器数据写入RTC闹钟设置RTC定时器设置…

2024.5.28晚训题解

提前预告&#xff0c;市赛初中组会考算法题&#xff0c;应该会有两道模板题 比如DFS BFS 二分 简单动态规划&#xff0c;虽然我们没学多久&#xff0c;但是模板题你还是要会写的 A题 编辑距离 动态规划 注意多组输入 #include<iostream> using namespace std; int dp[1…

9、C#【进阶】特性

特性 文章目录 1、特性概念2、自定义特性 Attribute3、特性的使用4、限制自定义特性的使用范围5、系统自带特性1、过时特性2、调用者信息特性3、条件编译特性4、外部dll包函数特性 1、特性概念 特性是一种允许我们向程序的程序集添加元数据的语言结构 它是用于保存程序机构信息…

【机器学习300问】103、简单的经典卷积神经网络结构设计成什么样?以LeNet-5为例说明。

一个简单的经典CNN网络结构由&#xff1a;输入层、卷积层、池化层、全连接层和输出层&#xff0c;这五种神经网络层结构组成。它最最经典的实例是LeNet-5&#xff0c;它最早被设计用于手写数字识别任务&#xff0c;包含两个卷积层、两个池化层、几个全连接层&#xff0c;以及最…

ansible批量漏洞升级openssh版本

1、ansible宿主机准备好环境&#xff0c;并写好hosts文件 [rootoxidized ansible]# cat hosts [all] 10.10.200.33 10.10.200.34 10.10.200.35跑playbook之前记得提前发送秘钥 ssh-copy-id 10.10.200.33/34/352、下载好安装包&#xff0c;然后编写yml [rootoxidized ansible]…

【实用的 IDEA 配置和操作技巧总结】

前置知识 IDEA的设置快捷键为ctrlalts键&#xff0c;后文介绍IDEA常见的配置就不再赘述这一点了。 基础配置 取消默认打开上次项目 日常开发都会打开不同的项目&#xff0c;初次安装IDEA之后&#xff0c;每次打开IDEA都会开启上一次启动的项目&#xff0c;所以我们需要进入设…

0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKB方案

大纲 序列化反序列化完整TypeHandlerSQL XML完整XML Mapper测试代码代码 在《0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKT方案》中&#xff0c;我们介绍WTK方案的优点&#xff0c;也感受到它的繁琐和缺陷。比如&#xff1a; 需要借助ST_GeomFromText…

350种类型、10W+量级的API,企业应该怎么管?

忽如一夜春风来&#xff0c;万物皆可API。 在互联网时代&#xff0c;API无处不在&#xff1a;企业对外开放的数据、服务和业务能力&#xff0c;以API的形式提供给合作方&#xff1b;企业内部应用与应用、App与App之间的通信&#xff0c;通过API进行&#xff1b;甚至应用内部的…

php 连接sqlserver步骤

1.首先要确定使用的是sqlserver的哪个版本&#xff0c;比如sqlserver2012 2.确定服务器是64位还是32位的 3.确认一下使用php的哪个版本&#xff0c;比如php7.1 SQL Server 的 Microsoft PHP 驱动程序 Microsoft Drivers for PHP 支持矩阵 - PHP drivers for SQL Server | Mi…