学习笔记--基于Sa-Token 实现Java项目单点登录+同端互斥检测

目录

同端互斥登录

单点登录SSO

架构选型

模式二: URL重定向传播

前后端分离

整体流程

准备工作

搭建客户端

搭建认证中心SSO Server

环境配置

开放认证接口

启动类

跨域处理

同端互斥登录

同端互斥登陆 模块

同端互斥登录指:同一类型设备上只允许单地点登录,在不同类型设备上允许同时在线。比如 QQ 可以手机电脑同时在线,但是不能在两个手机上同时登录一个账号。具体步骤如下

sa-token:# token 名称(同时也是 cookie 名称)token-name: praxisAI# token 有效期(单位:秒) 默认30天,-1 代表永久有效timeout: 2592000# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结active-timeout: -1# 是否开启同端互斥登录 (false开启)is-concurrent: false   # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)is-share: true# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)token-style: uuid# 是否输出操作日志is-log: true
  1. 在配置文件中,将 isConcurrent 配置为false

  2. DeviceUtils工具类:判断前端请求传来的设备信息,比如pc,后续设置到Sa-Token登录态中

//设备工具类
public class DeviceUtils {//根据请求获取设备信息public static String getRequestDevice(HttpServletRequest request) {String userAgentStr = request.getHeader(Header.USER_AGENT.toString());// 使用 Hutool 解析 UserAgentUserAgent userAgent = UserAgentUtil.parse(userAgentStr);ThrowUtils.throwIf(userAgent == null, ErrorCode.OPERATION_ERROR, "非法请求");// 默认值是 PCString device = "pc";// 是否为小程序if (isMiniProgram(userAgentStr)) {device = "miniProgram";} else if (isPad(userAgentStr)) {// 是否为 Paddevice = "pad";} else if (userAgent.isMobile()) {// 是否为手机device = "mobile";}return device;}/*** 判断是否是小程序* 一般通过 User-Agent 字符串中的 "MicroMessenger" 来判断是否是微信小程序**/private static boolean isMiniProgram(String userAgentStr) {// 判断 User-Agent 是否包含 "MicroMessenger" 表示是微信环境return StrUtil.containsIgnoreCase(userAgentStr, "MicroMessenger")&& StrUtil.containsIgnoreCase(userAgentStr, "MiniProgram");}/*** 判断是否为平板设备* 支持 iOS(如 iPad)和 Android 平板的检测**/private static boolean isPad(String userAgentStr) {// 检查 iPad 的 User-Agent 标志boolean isIpad = StrUtil.containsIgnoreCase(userAgentStr, "iPad");// 检查 Android 平板(包含 "Android" 且不包含 "Mobile")boolean isAndroidTablet = StrUtil.containsIgnoreCase(userAgentStr, "Android")&& !StrUtil.containsIgnoreCase(userAgentStr, "Mobile");// 如果是 iPad 或 Android 平板,则返回 truereturn isIpad || isAndroidTablet;}
}

 3. 登录时传入参数

// 使用 Sa-Token 登录,并指定设备,同端登录互斥
StpUtil.login(user.getId(), DeviceUtils.getRequestDevice(request));//例如 调用此方法登录后,同设备的会被顶下线(不同设备不受影响)
StpUtil.login(10001, "PC");     

Sa-Token 在背后做了大量的工作,包括

  1. 检查此账号是否之前已有登录,为账号生成 Token(uuid 风格) 凭证与 Session 会话

  2. 记录 Token 活跃时间;

  3. 通知全局侦听器,xx 账号登录成功;

  4. Token 注入到请求上下文返回给前端 等等其它工作……(利用cookie自动注入特性)

    1. Cookie 可以从后端控制往浏览器中写入 token 值。

    2. Cookie 会在前端每次发起请求时自动提交 token 值。

JWT token模式

//1、登录成功后:后端将 token 返回到前端
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();  //包括tokenName和tokenValue
return SaResult.data(tokenInfo);  //返回给前端 
​
//2.前端保存到localstorge中,下次请求带上
//3.后端Sa-Token 就能像传统PC端一样自动读取到 token 值,进行鉴权

记住我模式

原理:调用StpUtil.login(10001, true),在浏览器写入一个持久Cookie储存 Token,此时用户即使重启浏览器 Token 依然有效。

Sa-Token的登录授权,默认就是[记住我]模式,为了实现[非记住我]模式,你需要在登录时如下设置

// 设置登录账号id为10001,第二个参数指定是否为[记住我],当此值为false后,关闭浏览器后再次打开需要重新登录
StpUtil.login(10001, false);

注销模块

// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`:代表当前会话暂未登录
StpUtil.checkLogin();
// 移除登录态
StpUtil.logout();

获取当前登录用户

// 获取当前会话账号id, 如果未登录,则返回 null 
Object loginUserId = StpUtil.getLoginIdDefaultNull();
if (loginUserId == null) {throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
User currentUser = this.getById((String) loginUserId);
if (currentUser == null) {throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
return currentUser;
单点登录SSO

Sa-Token

单点登录举个例子理解

假设系统被切割为多个部分:商城、论坛、直播、社交…… 如果用户每访问一个模块都要登录一次,那么这样就很麻烦了, 为了优化用户体验,需要一套机制将这N个系统的认证授权互通共享,让用户在一个系统登录之后,可以畅通无阻的访问其它所有系统。

凡是稍微上点规模的系统,统一认证中心都是绕不过去的槛。

架构选型
系统架构采用模式简介文档链接
前端同域 + 后端同 Redis模式一共享 Cookie 同步会话文档、示例
前端不同域 + 后端同 Redis模式二URL重定向传播会话文档、示例
前端不同域 + 后端不同 Redis模式三Http请求获取会话文档、示例
  1. 前端同域:指多个系统可以部署在同一个主域名之下,比如:c1.domain.comc2.domain.comc3.domain.com

  2. 后端同Redis:指多个系统可以连接同一个Redis,PS:这里并不需要把所有项目的数据都放在同一个Redis中,Sa-Token提供了 [权限缓存与业务缓存分离] 的解决方案,详情戳: Alone独立Redis插件。

  3. 模式三:Http请求获取会话(Sa-Token对SSO提供了完整的封装,只需要复制几段代码便可以轻松集成)

根据本项目场景,后端同Redis,所以选择模式二+ 前后端分离架构

SaToken SSO优势

  1. API 简单易用,且官方文档介绍很详细。

  2. 支持三种模式,是否跨域、是否共享Redis、是否前后端分离 等场景很轻松解决

  3. 内置域名校验、密钥校验、Token防窃取,安全性很高

  4. 不丢参数,相比其他单点登录框架,Sa-Token-SSO有专门的算法保证了参数不丢失,登录成功之后原路返回页面。

模式二: URL重定向传播

如果多个系统部署在不同的域名之下,但是后端可以连接同一个Redis,那么便可以使用 [URL重定向传播会话] 的方式做到单点登录

首先我们再次复习一下,多个系统之间为什么无法同步登录状态?

  1. 前端的Token无法在多个系统下共享。

  2. 后端的Session无法在多个系统间共享。

第二点官方已使用 Alone独立Redis插件 做到权限缓存直连 SSO-Redis 数据中心,不再赘述。

  1. 未登录,客户端页面显示“登录”链接。

  2. 点击登录,携带当前页面back参数(包含客户端host),跳转到当前客户端的/sso/login接口

    1. 未登录时,重定向到SSO服务器的登录页

    2. 用户在SSO登录页登录成功后,SSO服务器返回ticket,重定向回指定客户端

  3. 客户端通过SaSsoClientProcessor向SSO服务器验证ticket的有效性,获取用户ID

  4. 使用StpUtil.login(userId)在本地登录用户

在跨域模式下, "共享Cookie方案" 的失效,所以必须采用一种新的方案来传递Token。

前后端分离

首先理解对于一个前后端分离项目,即我们的系统,整体流程是这样的

前端==>后端==>重定向到SSO认证中心(展示页面,所以SSO服务器代码是前后端不分离的,主要作用就是:展示登录页,校验登录和重定向

这种情况要考虑跨域,即客户端与 SSO 服务器部署在不同域,缺点:前端需主动调用 /sso/getSsoAuthUrl 获取登录地址,并处理重定向逻辑

整体流程
  1. 前端页面点击 [登录] 后触发调用登录函数,调用后端接口/sso/login,并携带back参数( 包含客户端host )

    • 形如:http://{sso-client}/sso/login?back=xxx

  2. 若子系统检测到此用户尚未登录,则直接重定向到SSO认证中心,并携带redirect参数(记录子系统的登录页URL

    • 形如:http://{sso-server}/sso/auth?redirect=xxx?back=xxx

  3. 用户进入 SSO认证中心 的登录页面,开始登录。

  4. 用户 输入账号密码 并 登录成功,SSO认证中心下放ticket码参数。重定向回客户端的登录接口/sso/login

    • 形如:http://{sso-client}/sso/login?back=xxx&ticket=xxxxxxxxx

  5. 客户端通过SaSsoClientProcessor向SSO服务器验证ticket的有效性,获取用户ID,使用StpUtil.login(userId)在本地登录用户

  6. 其他客户端尝试登录后,前面步骤一样,到了请求发到SSO服务器,会通过sso/auth判断是否已经登录(已登录内部是通过redis查看用户id来判断的)然后直接返回Ticket进行登录即可

整个过程,除了第四步用户在SSO认证中心登录时会被打断,其余过程均是自动化的

当用户在另一个子系统再次点击[登录]按钮,由于此用户在SSO认证中心已有会话存在, 所以第四步也将自动化,也就是单点登录的最终目的 一次登录,处处通行。

流程如下

准备工作

首先修改hosts文件(C:\Windows\System32\drivers\etc\hosts),添加以下IP映射,方便测试:

127.0.0.1 sa-sso-server.com    #ip映射,用于测试多个客户端单点登录
127.0.0.1  sa-sso-client1.com   
127.0.0.1 sa-sso-client2.com
搭建客户端

客户端就是我们的后端服务器,考虑到前后分离架构,比如用VUE3前端,下面是一个模拟的前端例子

<!-- 项目首页 -->
<template><h2> Sa-Token SSO-Client 应用端(前后端分离版-Vue3) </h2><p>当前是否登录:<b>{{isLogin}}</b></p><p><router-link :to="loginUrl">登录</router-link>&nbsp;&nbsp;  <!--点击登录后携带back参数请求后端sso/login接口--></p>
</template><script setup>
import { ref } from 'vue'
import {baseUrl, ajax} from './method-util.js'// 单点登录地址
const loginUrl = '/api/sso/login?back=' + encodeURIComponent(location.href);
// 是否登录
const isLogin = ref(false);// 1.查询当前会话是否登录
ajax('/api/sso/isLogin', {}, function (res) {console.log('/isLogin 返回数据:', res);isLogin.value = res.data;
})
</script>
<!-- Sa-Token-SSO-Client端-登录页 -->
<template>
</template>
<script setup>
import {onMounted} from "vue";
import {ajax, getParam} from './method-util.js';
import router from '../router';// 获取参数
const back = getParam('back') || router.currentRoute.value.query.back;
const ticket = getParam('ticket') || router.currentRoute.value.query.ticket;console.log('获取 back 参数:', back)
console.log('获取 ticket 参数:', ticket)// 页面加载后触发
onMounted(() => {if(ticket) {    //2.如果ticket存在,则尝试通过ticket登录doLoginByTicket(ticket);} else {goSsoAuthUrl();  //3.不存在,则重定向至认证中心}
})// 重定向至认证中心方法
function goSsoAuthUrl() {ajax('/api/sso/getSsoAuthUrl', {clientLoginUrl: location.href}, function(res) {console.log('/api/sso/getSsoAuthUrl 返回数据', res);location.href = res.data;})
}// 根据ticket值登录 方法
function doLoginByTicket(ticket) {ajax('/api/sso/doLoginByTicket', {ticket: ticket}, function(res) {console.log('/api/sso/doLoginByTicket 返回数据', res);if(res.code === 200) {localStorage.setItem('satoken', res.data);location.href = decodeURIComponent(back);} else {alert(res.msg);}})
}
</script>

搭建后端

环境配置

<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc --><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.40.0</version></dependency><!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) --><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-redis-jackson</artifactId><version>1.40.0</version></dependency><!-- 提供Redis连接池 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- Sa-Token 插件:整合SSO --><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-sso</artifactId><version>1.40.0</version></dependency><!-- Sa-Token插件:权限缓存与业务缓存分离 --><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-alone-redis</artifactId><version>1.40.0</version></dependency>

配置文件中整合sso-client相关配置+API密钥+redisticket

sa-token:# token 名称(同时也是 cookie 名称)token-name: praxisAI# token 有效期 2天(单位:秒) 默认30天,-1 代表永久有效timeout: 172800# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结active-timeout: -1# 是否开启同端互斥登录 (false开启)is-concurrent: false# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)is-share: true# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)token-style: uuid# 是否输出操作日志is-log: true# SSO-相关配置sso-client:# SSO-Server 端主机地址server-url: http://sa-sso-server.com:9000sign:# API 接口调用秘钥,确保和服务端一致,sa-token官方文档会给secret-key: xxxx# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)alone-redis:# Redis数据库索引 (默认为0)database: 1# Redis服务器地址host: xxx# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)password: xxxtimeout: 10slettuce:pool:# 连接池最大连接数max-active: 200# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0
forest:# 关闭 forest 请求日志打印log-enabled: false

新建Controller作为客户端接口,用于重定向到SSO服务器和接收服务器参数

//前后台分离架构下集成SSO所需的代码 (SSO-Client端)
@RestController
public class H5Controller {// 当前是否登录@GetMapping("/sso/isLogin")public Object isLogin() {return SaResult.data(StpUtil.isLogin());}// 返回SSO认证中心登录地址 @GetMapping("/sso/getSsoAuthUrl")public SaResult getSsoAuthUrl(String clientLoginUrl) {String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, "");return SaResult.data(serverAuthUrl);}// 根据ticket进行登录@PostMapping("/sso/doLoginByTicket")public SaResult doLoginByTicket(String ticket) {SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket, "/api/sso/doLoginByTicket");StpUtil.login(ctr.loginId, ctr.remainSessionTimeout);return SaResult.data(StpUtil.getTokenValue());}// 全局异常拦截 @ExceptionHandlerpublic SaResult handlerException(Exception e) {e.printStackTrace(); return SaResult.error(e.getMessage());}	
}
搭建认证中心SSO Server

新建一个springboot项目作为认证中心,本项目后端系统算做一个客户端client,后续引入多个后端系统,也可以按照客户端方式构建

环境配置
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.40.0</version>
</dependency>
<!-- Sa-Token 插件:整合SSO -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-sso</artifactId><version>1.40.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-redis-jackson</artifactId><version>1.40.0</version>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>
<!-- redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- 除此之外还需要引入mysql数据库的一些依赖,因为要在认证中心实现登录校验 -->
# 端口
server:port: 9000
# Sa-Token 配置
sa-token:# ------- SSO-模式二相关配置 sso-server:# Ticket有效期 (单位: 秒),默认五分钟 ticket-timeout: 300# 所有允许的授权回调地址allow-url: "http://sa-sso-client1/api/sso/login,http://sa-sso-client2/api/sso/login"sign:# API 接口调用秘钥secret-key: xxx
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://xxx/xxxusername: xxxpassword: xxx# Redis配置 (SSO模式一和模式二使用Redis来同步会话)redis:# Redis数据库索引(默认为0)database: 1# Redis服务器地址host: xxx# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)password: xxx# 连接超时时间timeout: 10slettuce:pool:# 连接池最大连接数max-active: 200# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0forest: # 关闭 forest 请求日志打印log-enabled: false
开放认证接口

新建 SsoServerController,用于对外开放接口,先直接拉官方server-demo,在修改

//SSO Server端 Controller
@RestController
public class SsoServerController {//处理所有SSO相关请求:拆分式路由// SSO-Server:统一认证地址,接受参数:redirect=授权重定向地址// 作用: 用户未登录,重定向到登陆页面//    ** 已登录,生成Ticket重定向回客户端**(已登录内部是通过redis查看用户id来判断的)@RequestMapping("/sso/auth")public Object ssoAuth() {return SaSsoServerProcessor.instance.ssoAuth();}// SSO-Server:RestAPI 登录接口,账号密码登录接口,接受参数:name、pwd// 作用: 处理登录表单提交,调用doLoginHandle进行验证//      验证成功后生成SSO票据并返回给客户端@RequestMapping("/sso/doLogin")public Object ssoDoLogin() {return SaSsoServerProcessor.instance.ssoDoLogin();}/*** 盐值,混淆密码*/public static final String SALT = "kk";@Resourceprivate UserService userService;// 配置SSO相关参数 @Autowiredprivate void configSso(SaSsoServerConfig ssoServer) {// 自定义API地址,用于修改统一认证中心的地址//SaSsoServerProcessor.instance.ssoServerTemplate.apiName.ssoAuth = "/sso/auth2";// 配置:未登录时返回的ViewssoServer.notLoginView = () -> {return new ModelAndView("sa-login.html");};// 配置:登录处理函数  参数:账号密码ssoServer.doLoginHandle = (name, pwd) -> {// 1. 校验if (StringUtils.isAnyBlank(name, pwd)) {return SaResult.error("参数为空");}if (name.length() < 4) {return SaResult.error( "账号错误");}if (pwd.length() < 8) {return SaResult.error("密码错误");}// 2. 加密String encryptPassword = DigestUtils.md5DigestAsHex((SALT + pwd).getBytes());// 查询用户是否存在QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("userAccount", name);queryWrapper.eq("userPassword", encryptPassword);User user = userService.getUserInfo(queryWrapper);// 用户不存在if (user == null) {// 登录失败,重定向到 /sso/auth 并携带错误参数return "redirect:/sso/auth?error=用户不存在或密码错误";}// 获取当前请求HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String device = DeviceUtils.getRequestDevice(request);// 使用 Sa-Token 登录,并指定设备,同端登录互斥StpUtil.login(user.getId(), device);StpUtil.getSession().set("user_login", user);return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());};}// 在 SsoServerController 或全局配置中添加@Beanpublic SaServletFilter saServletFilter() {return new SaServletFilter().setBeforeAuth(obj -> {// 设置响应头防止缓存SaHolder.getResponse().setHeader("Cache-Control", "no-cache, no-store, must-revalidate");SaHolder.getResponse().setHeader("Pragma", "no-cache");SaHolder.getResponse().setHeader("Expires", "0");});}
}
启动类
@SpringBootApplication
public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args);}System.out.println();System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------");System.out.println("配置信息:" + SaSsoManager.getServerConfig());System.out.println();
}
跨域处理

配置项 sa-token.sso-server.allow-url=* 意为配置所有允许的Client端授权地址,不在此配置项中的URL将无法单点登录成功

但是,在生产环境中,此配置项绝对不能配置为 * ,否则会有被Ticket劫持的风险

假设攻击者根据模仿我们的授权地址,巧妙的构造一个URL

http://sa-sso-server.com:9000/sso/auth?redirect=https://www.baidu.com/

当不知情的小白被诱导访问了这个URL时,它将被重定向至百度首页,代表着用户身份的Ticket码也显现到了URL之中

防范处理

redirect参数进行校验,如果其不在指定的URL列表中时,拒绝下放ticket

#SSO服务器端进行配置
sa-token: sso-server: # 配置允许单点登录的 url   allow-url: "http://localhost:8101/sso/login"  #配置到详细地址  

为什么不直接回传 Token,而是先回传 Ticket,再用 Ticket 去查询对应的账号id?

Token 作为长时间有效的会话凭证,任何时候都不应该直接暴露在 URL 之中(虽然 Token 很安全,但会直接暴露为很多漏洞提供可乘之机)

为了让系统绝对安全,选择先回传 Ticket,再由Ticket获取账号id,而且 Ticket 一次性用完即废,提高安全性。

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

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

相关文章

本地生活服务APP开发,市场发展全新商业机遇

随着移动互联网的快速发展&#xff0c;人们的消费和生活习惯发生了巨大改变&#xff0c;本地生活服务市场迎来了发展爆发期&#xff01;从外卖、团购等&#xff0c;人们越来越依赖通过手机APP解决日常生活中的各种需求。对于企业而言&#xff0c;一款完善、多样、便捷的本地生活…

当科技业成为系统性压榨的绞肉机

深夜的硅谷办公室依然灯火通明&#xff0c;键盘敲击声此起彼伏。一位程序员在Slack上收到主管的紧急需求&#xff1a;“这个功能明早必须上线。”他苦笑一声&#xff0c;关掉手机里名为“缓解焦虑”的冥想App——这已是本周第三次被迫服用公司提供的“心灵解药”。此刻&#xf…

代码随想录算法训练营第五十六天 | 108.冗余连接 109.冗余连接II

108. 冗余连接 卡码网题目链接&#xff08;ACM模式&#xff09;(opens new window) 题目描述 有一个图&#xff0c;它是一棵树&#xff0c;他是拥有 n 个节点&#xff08;节点编号1到n&#xff09;和 n - 1 条边的连通无环无向图&#xff08;其实就是一个线形图&#xff09;…

什么是索引?为什么要使用B树作为索引数据结构?

MySQL的事务特性 1.原子性:原子性就是这个事件要么执行完,要么没执行,不会存在中间状态,与C中华那个加锁避免多线程竞争是一个道理; 2.一致性:保持事件的操作对象双方某数据之和是不变的,就以转账为例,A转给B100块,那么A的余额多100,B的余额就必须少100; 3.隔离性:隔离就是独…

pyqt5报错:qt.qpa.plugin: Could not find the Qt platform plugin “xcb“(已解决)

我在使用pyqt库的时候报错&#xff1a; qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in \ "/mnt/private_disk/anaconda3/envs/aot-manip/lib/python3.8/site-packages/PyQt5/Qt5/plugins/platforms" even though it was found. This ap…

AI大模型全攻略:原理 · 部署 · Prompt · 场景应用

🚀 AI大模型全攻略:原理 部署 Prompt 场景应用 本文从基础原理到实践部署,再到 Prompt 工程与典型应用案例,全方位解析 AI 大模型的学习路径与使用方法,适合开发者、产品经理、技术爱好者等不同背景读者。 🧠 一、什么是 AI 大模型? AI 大模型(Large Language Mo…

2024年MathorCup数学建模D题量子计算在矿山设备配置及运营中的建模应用解题文档与程序

2024年第十四届MathorCup高校数学建模挑战赛 D题 量子计算在矿山设备配置及运营中的建模应用 原题再现&#xff1a; 随着智能技术的发展&#xff0c;智慧矿山的概念越来越受到重视。越来越多的设备供应商正在向智慧矿山整体解决方案供应商转型&#xff0c;是否具备提供整体解…

Flink 流处理框架的核心特性

文章目录 事件时间支持Flink状态编程一、状态的类型1. 托管状态&#xff08;Managed State&#xff09;2. 原始状态&#xff08;Raw State&#xff09; 二、状态的管理和容错 Flink端到端的一致性1、检查点机制2、幂等3、事务 水位线窗口操作1、窗口类型2、窗口操作的时间语义 …

交换机(access端口)

任务&#xff1a;对access有更深入的理解 通过网盘分享的文件&#xff1a;交换机&#xff08;access&#xff09;.zip 链接: https://pan.baidu.com/s/1cMC6Na_1PLo6zOHazFplQQ?pwd23a5 提取码: 23a5 SW1 <Huawei>sys [Huawei]dis vlan The total number of vlans …

《鸟哥的Linux私房菜基础篇》---5 vim 程序编辑器

目录 一、vim程序编辑器的简介 二、命令模式快捷键&#xff08;默认模式&#xff09; 1、光标移动 2、编辑操作 3、搜索与替换 三、插入模式快捷键 四、底行模式快捷键&#xff08;按&#xff1a;进入&#xff09; 五、高级技巧 1、分屏操作 2、多文件编辑 3、可视化…

AI大白话(四):自然语言处理——AI是如何理解和生成人类语言的?

🌟引言: 专栏:《AI大白话》 AI大白话(一):5分钟了解AI到底是什么? AI大白话(二):机器学习——AI是怎么“学习“的? AI大白话(三):深度学习——AI的‘大脑‘是如何构建的? 大家好!欢迎回到"AI大白话"系列。前面我们聊了AI的基本概念、机器学习的原理…

扩展卡尔曼滤波

1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统&#xff0c;扩展卡尔曼滤波 则扩展到了非线性系统&#xff0c;核心原理就是将非线性系统线性化&#xff0c;主要用的的知识点是 泰勒展开&#xff08;我另外一篇文章的链接&#xff09;&#xff0c;如下是泰勒展开的公式…

安装unsloth

我在llamafactory微调LLM&#xff0c;简单测了一些&#xff08;很不精准&#xff09;&#xff0c;加速方法中unsloth比flash_attention速度快了40%&#xff0c;显存占用减少15%&#xff1b; 创建虚拟环境&#xff1a;conda create -n env_name python3.10, 然后conda activate…

关于 51 单片机显示多个数码管时出现残影

残影现象&#xff1a; 出现残影代码&#xff1a; #include <REGX52.H> #include <INTRINS.H> void Delayxms(unsigned int x) //11.0592MHz {while(x){unsigned char i, j;_nop_();i 2;j 199; do{while (--j);} while (--i);x--;} } void DisplayDigitalNumb…

STM32学习笔记之常用外设接口(原理篇)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

InnoDB 引擎核心知识点

InnoDB 引擎核心知识点 6.1 逻辑存储结构 表空间&#xff08;Tablespace&#xff09;&#xff1a;所有数据逻辑上存储在一个表空间中&#xff0c;物理上可能由多个文件组成。段&#xff08;Segment&#xff09;&#xff1a;分为数据段&#xff08;B树叶子节点&#xff09;、索引…

深度学习 Deep Learning 第9章 卷积网络 CNN

深度学习 Deep Learning 第9章 卷积网络 章节概述 本章深入探讨了卷积网络的原理、变体及其在深度学习中的应用。卷积网络通过卷积操作实现了参数共享和稀疏连接&#xff0c;显著提高了模型的效率和性能。本章首先介绍了卷积操作的基本形式及其在不同数据维度上的应用&#x…

基于MATLAB的涡旋光和高斯光叠加产生平顶光

强度叠加耦合成平顶光&#xff0c;不发生干涉 通过分别生成高斯光和涡旋光的强度分布&#xff0c;然后按合适的权重将它们叠加&#xff0c;得到近似平顶光&#xff08;flat‐top beam&#xff09;的效果。由于我们只是将强度相加&#xff08;而非复振幅叠加&#xff09;&#…

wordpress-网站百宝箱插件

含置顶,网页宠物, 哀悼, 禁止复制, 禁止查看源码, 弹幕, WP优化,媒体分类,预加载,定时发布,在线客服, 留言板, 手机客服, 网站背景, 公告, 跑马灯, 水印, 分享, 打赏, 海报图, 广告,数据库管理,图片加载特效。等综合功能插件

北斗导航 | 基于北斗三号短报文通信的北斗-YOLO融合系统原理,算法公式,系统流程框图,matlab代码,应用场景

以下是关于基于北斗三号短报文通信的北斗-YOLO融合系统的详细解析,包含原理、算法公式、系统流程、Matlab代码框架和应用场景。一、系统原理 北斗-YOLO融合系统结合了北斗三号短报文通信(双向通信能力)和YOLO目标检测算法,用于在无地面网络覆盖区域实现实时目标检测与数据传…