ruoyi-cloud登录接口实现滑块验证码

一、前言

ruoyi项目默认的验证码是这样的
在这里插入图片描述

今天来尝试增加滑块验证码,我们用到的是tianai-captcha
文档地址:http://doc.captcha.tianai.cloud/
源码地址:https://gitee.com/tianai/tianai-captcha

下面来看具体的步骤。

二、后端

gateway中引入依赖

<dependency><groupId>cloud.tianai.captcha</groupId><artifactId>tianai-captcha-springboot-starter</artifactId><version>1.4.1</version>
</dependency>

并增加相应的配置

# 客户端验证码
captcha:cache:enabled: truecache-size: 20# 二次验证secondary:enabled: false# 是否初始化默认资源init-default-resource: false

gateway中新增加一个SliderCaptchaHandler处理类

import cloud.tianai.captcha.spring.application.ImageCaptchaApplication;
import cloud.tianai.captcha.spring.vo.CaptchaResponse;
import cloud.tianai.captcha.spring.vo.ImageCaptchaVO;
import com.iinplus.common.core.exception.CaptchaException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;import javax.annotation.Resource;/*** 验证码获取*/
@Component
public class SliderCaptchaHandler implements HandlerFunction<ServerResponse> {@Resourceprivate ImageCaptchaApplication sca;@Overridepublic Mono<ServerResponse> handle(ServerRequest serverRequest) {CaptchaResponse<ImageCaptchaVO> res;try {// 1.生成滑块验证码(该数据返回给前端用于展示验证码数据)res = sca.generateCaptcha();} catch (CaptchaException e) {return Mono.error(e);}return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(res));}
}

RouterFunctionConfiguration中新增一个路由

@Resource
private SliderCaptchaHandler sliderCaptchaHandler;@Bean
public RouterFunction routerFunc() {return RouterFunctions.route(RequestPredicates.GET("/captcha").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),            sliderCaptchaHandler);
}

新增一个filter类,用来验证图形验证码。

import cloud.tianai.captcha.common.response.ApiResponse;
import cloud.tianai.captcha.spring.application.ImageCaptchaApplication;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.iinplus.common.core.exception.CaptchaException;
import com.iinplus.common.core.utils.ServletUtils;
import com.iinplus.common.core.utils.StringUtils;
import com.iinplus.gateway.config.properties.CaptchaProperties;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;import javax.annotation.Resource;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;/*** 验证码过滤器*/
@Component
public class ValidateCaptchaFilter extends AbstractGatewayFilterFactory<Object> {private final static String[] VALIDATE_URL = new String[]{"/system/login"};@Resourceprivate ImageCaptchaApplication sca;@Resourceprivate CaptchaProperties captchaProperties;private static final String TRACK = "sliderCaptchaTrack";private static final String UUID = "id";@Overridepublic GatewayFilter apply(Object config) {return (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();// 非登录/注册请求或验证码关闭,不处理List<String> list = Arrays.asList(VALIDATE_URL);// 请求地址String url = request.getURI().getPath();if (!StringUtils.matches(url, list) || !captchaProperties.getEnabled()) {return chain.filter(exchange);}try {String rspStr = resolveBodyFromRequest(request);if (StringUtils.isEmpty(rspStr)) {throw new CaptchaException("验证码不能为空");}JSONObject obj = JSON.parseObject(rspStr);if (!obj.containsKey(UUID) || !obj.containsKey(TRACK)) {throw new CaptchaException("验证码不能为空");}String id = obj.getString(UUID);ImageCaptchaTrack sliderCaptchaTrack = obj.getObject(TRACK, ImageCaptchaTrack.class);ApiResponse<?> match = sca.matching(id, sliderCaptchaTrack);if (!match.isSuccess()) {throw new CaptchaException(match.getMsg());}} catch (Exception e) {e.printStackTrace();return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());}return chain.filter(exchange);};}private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {// 获取请求体Flux<DataBuffer> body = serverHttpRequest.getBody();AtomicReference<String> bodyRef = new AtomicReference<>();body.subscribe(buffer -> {CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());DataBufferUtils.release(buffer);bodyRef.set(charBuffer.toString());});return bodyRef.get();}
}

注意:其中/system/login为登录验证的路径,需要在网关中配置,并且需要加入白名单。

如果需要修改图形验证码默认的背景图

import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.common.constant.SliderCaptchaConstant;
import cloud.tianai.captcha.resource.common.model.dto.Resource;
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
import cloud.tianai.captcha.resource.impl.DefaultResourceStore;
import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import org.springframework.stereotype.Component;import static cloud.tianai.captcha.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH;/*** 自定义背景图片*/
@Component
public class ResourceStore extends DefaultResourceStore {public ResourceStore() {// 滑块验证码 模板 (系统内置)ResourceMap template1 = new ResourceMap("default", 4);template1.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/active.png")));template1.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/1/fixed.png")));ResourceMap template2 = new ResourceMap("default", 4);template2.put(SliderCaptchaConstant.TEMPLATE_ACTIVE_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/active.png")));template2.put(SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_TEMPLATE_PATH.concat("/2/fixed.png")));// 1. 添加一些模板addTemplate(CaptchaTypeConstant.SLIDER, template1);addTemplate(CaptchaTypeConstant.SLIDER, template2);// 2. 添加自定义背景图片addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/1.png", "default"));addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/2.png", "default"));addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/3.png", "default"));addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/4.png", "default"));addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/5.png", "default"));addResource(CaptchaTypeConstant.SLIDER, new Resource("classpath", "bg/6.png", "default"));}
}

图片的路径如下
在这里插入图片描述

网关配置增加filters,把ValidateCaptchaFilter加上才生效

spring:cloud:gateway:discovery:... ...routes:# 系统模块- id: systemuri: lb://systempredicates:- Path=/system/**filters:# 验证码处理- ValidateCaptchaFilter- StripPrefix=1

system模块的登录验证

/*** 系统用户登录*/
@RestController
public class TokenController {@Autowiredprivate SysLoginService sysLoginService;@PostMapping("login")public RpcResult<?> login(@RequestBody @Validated LoginBody form) {   LoginUser userInfo = sysLoginService.login(form);... ...return RpcResult.success(rspMap);}
}
import lombok.Data;
import javax.validation.constraints.NotBlank;@Data
public class LoginBody {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")private String password;
}

三、前端

components下增加一个组件SliderCaptcha

<template><div class="slider rotate"><div class="content"><div class="bg-img-div"><img :src="captcha.backgroundImage" id="imgId" alt/><canvas id="canvasId" ref="canvas"></canvas></div><div class="rotate-img-div" :style="rotateImgDiv"><img :src="captcha.templateImage" alt/></div></div><div class="slider-move"><div class="slider-move-track">拖动滑块到正确位置</div><div class="slider-move-btn" :style="sliderMoveBtn" @mousedown="down" @touchstart="down"></div></div><div class="bottom"><div class="close-btn" @click="close()"></div><div class="refresh-btn" @click="refreshCaptcha"></div></div></div>
</template><script>
export default {name: "Slider",props: {captcha: {type: Object},},data() {return {currentCaptchaConfig: {},sliderMoveBtn: "",rotateImgDiv: "",checkParam: {}}},mounted() {this.initCaptcha();},methods: {refreshCaptcha() {this.$emit("init");this.initCaptcha();},initCaptcha() {this.sliderMoveBtn = "background-position: -5px 11.79625%; transform: translate(0, 0)";this.rotateImgDiv = "transform: translate(0, 0)";this.currentCaptchaConfig = {};let bgImageWidth = this.$refs.canvas.offsetWidth;let bgImageHeight = this.$refs.canvas.offsetHeight;this.checkParam = {bgImageWidth: bgImageWidth,bgImageHeight: bgImageHeight,startSlidingTime: new Date(),endSlidingTime: undefined,trackList: [],}},down(event) {let targetTouches = event.originalEvent ? event.originalEvent.targetTouches : event.targetTouches;let startX = event.pageX;let startY = event.pageY;if (startX === undefined) {startX = Math.round(targetTouches[0].pageX);startY = Math.round(targetTouches[0].pageY);}this.currentCaptchaConfig.startX = startX;this.currentCaptchaConfig.startY = startY;const pageX = this.currentCaptchaConfig.startX;const pageY = this.currentCaptchaConfig.startY;const startSlidingTime = this.checkParam.startSlidingTime;const trackList = this.checkParam.trackList;trackList.push({x: pageX - startX,y: pageY - startY,type: "down",t: (new Date().getTime() - startSlidingTime.getTime())});// pcwindow.addEventListener("mousemove", this.move);window.addEventListener("mouseup", this.up);// 手机端window.addEventListener("touchmove", this.move, false);window.addEventListener("touchend", this.up, false);this.sliderMoveBtn = `background-position:-5px 31.0092%`;},move(event) {if (event instanceof TouchEvent) {event = event.touches[0];}let pageX = Math.round(event.pageX);let pageY = Math.round(event.pageY);const startX = this.currentCaptchaConfig.startX;const startY = this.currentCaptchaConfig.startY;const startSlidingTime = this.checkParam.startSlidingTime;const end = 305;const trackList = this.checkParam.trackList;let moveX = pageX - startX;const track = {x: pageX - startX,y: pageY - startY,type: "move",t: (new Date().getTime() - startSlidingTime.getTime())};trackList.push(track);if (moveX < 0) {moveX = 0;} else if (moveX > end + 5) {moveX = end;}this.sliderMoveBtn = `transform:translate(${moveX}px, 0)`;this.rotateImgDiv = `transform:translate(${moveX}px, 0);`;},up(event) {window.removeEventListener("mousemove", this.move);window.removeEventListener("mouseup", this.up);// 手机端window.removeEventListener("touchmove", this.move);window.removeEventListener("touchend", this.up);if (event instanceof TouchEvent) {event = event.changedTouches[0];}let pageX = Math.round(event.pageX);let pageY = Math.round(event.pageY);const startX = this.currentCaptchaConfig.startX;const startY = this.currentCaptchaConfig.startY;const startSlidingTime = this.checkParam.startSlidingTime;const trackList = this.checkParam.trackList;const track = {x: pageX - startX,y: pageY - startY,type: "up",t: (new Date().getTime() - startSlidingTime.getTime())}trackList.push(track);this.checkParam.endSlidingTime = new Date();// 校验this.checkCaptcha()},close() {this.$emit("close");},checkCaptcha() {//this.checkParam = {};this.$emit("checkParam", this.checkParam)this.$emit("login");}},
}
</script>
<style scoped>
.slider {background-color: #fff;width: 380px;height: 340px;z-index: 999;box-sizing: border-box;padding: 9px;border-radius: 6px;box-shadow: 0 0 11px 0 #999999;
}.slider .content {width: 100%;height: 160px;position: relative;
}.bg-img-div {width: 100%;height: 100%;position: absolute;transform: translate(0px, 0px);
}.bg-img-div img {width: 100%;
}.bg-img-div canvas {width: 100%;position: absolute;left: 0;top: 0;
}.slider .slider-move {height: 60px;width: 100%;margin: 0;position: relative;top: 80px
}.slider .bottom {height: 25px;width: 100%;margin: 65px 10px 10px 0;
}.refresh-btn, .close-btn, .slider-move-btn {background: url(../../assets/images/sprite.1.2.4.png) no-repeat;
}.refresh-btn, .close-btn {display: inline-block;
}.slider-move .slider-move-track {line-height: 38px;font-size: 14px;text-align: center;white-space: nowrap;color: #88949d;-moz-user-select: none;-webkit-user-select: none;user-select: none;border-radius: 50px;background: #dfe1e2;width: 100%;
}.slider {user-select: none;
}.slider-move .slider-move-btn {transform: translate(0, 0);background-position: -5px 11.79625%;position: absolute;top: -12px;left: 0;width: 100%;height: 100%;
}.slider-move-btn:hover, .close-btn:hover, .refresh-btn:hover {cursor: pointer
}.bottom .close-btn {width: 25px;height: 25px;background-position: 0 44.86874%;margin: 10px 10px 10px 5px;float: left;
}.bottom .refresh-btn {width: 25px;height: 25px;background-position: 0 81.38425%;margin: 7px 10px 10px 2px;float: left;
}.rotate-img-div {height: 140%;position: absolute;transform: translate(0, 0);
}.rotate-img-div img {height: 100%;
}
</style>

修改登录和获取验证码的方法

// 登录方法
export function login(data) {return request({url: '/system/login',headers: {isToken: false},method: 'post',data: data})
}
// 获取验证码
export function captcha() {return request({url: '/captcha',headers: {isToken: false},method: 'get',timeout: 20000})
}

修改login.vue页面

<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">xxxx管理系统</h3><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"auto-complete="off"placeholder="账号"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off"placeholder="密码"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><!--注释掉原来的验证码--><!--<el-form-item prop="code" v-if="captchaEnabled" style="margin: 10px 0"><el-inputv-model="loginForm.code"auto-complete="off"placeholder="验证码"style="width: 68%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item>--><el-form-item style="width:100%; margin: 10px 0"><el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox></el-form-item><el-form-item style="width:100%;margin-bottom: 10px"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="initCaptcha"><span v-if="!loading">登 录</span><span v-else>登 录 中...</span></el-button></el-form-item></el-form><!-- 滑块验证码,通过show来控制显示遮蔽层--><div v-if="show" class="mask"><SliderCaptchav-if="captchaVisible"ref="sliderCaptcha":captcha="captcha"@init="initCaptcha"@close="hideCaptcha"@login="handleLogin"/></div><!--  底部  --><div class="el-login-footer"><span>Copyright © xxx All Rights Reserved.</span></div></div>
</template><script>
import {captcha} from '@/api/login'
import Cookies from "js-cookie";
import {decrypt, encrypt} from '@/utils/jsencrypt'
import SliderCaptcha from '@/components/SliderCaptcha'export default {name: "Login",components: {SliderCaptcha},data() {return {// codeUrl: "",show: false,captcha: {},captchaVisible: false,loginForm: {username: undefined,password: undefined,rememberMe: false,//code: "",//uuid: "",// 增加下面两个属性ImageCaptchaTrack: {},id: ''},loginRules: {username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],// 不再需要这个验证// code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,// 验证码开关captchaEnabled: true,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},created() {//this.getCode();this.getCookie();},methods: {//注释原先的获取验证码方法/*getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},*/getCookie() {const username = Cookies.get("username");const password = Cookies.get("password");const rememberMe = Cookies.get('rememberMe')this.loginForm = {username: username === undefined ? this.loginForm.username : username,password: password === undefined ? this.loginForm.password : decrypt(password),rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)};},hideCaptcha() {this.captchaVisible = falsethis.show = false;},// 获取图形验证码initCaptcha() {this.$refs.loginForm.validate(valid => {if (valid) {captcha().then(res => {if (res) {this.captcha = res["captcha"];this.loginForm.id = res["id"];this.captchaVisible = truethis.show = true;}})}})},// 登录方法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.loginForm.sliderCaptchaTrack = this.$refs.sliderCaptcha.checkParamthis.$store.dispatch("Login", this.loginForm).then(() => {// console.info("this.redirect", this.redirect)this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;//调用子组件的刷新图形验证码的方法this.$refs.sliderCaptcha.refreshCaptcha()});}});}}
};
</script><style rel="stylesheet/scss" lang="scss">
<!--新增遮蔽层,其他省略-->
.mask {box-sizing: border-box;position: fixed;top: 0;left: 0;bottom: 0;right: 0;z-index: 1001;background: rgba(0, 0, 0, 0.3);transition: all 0.5s;display: flex;flex-direction: column;justify-content: center;align-items: center;
}
</style>

最后this.$store.dispatch("Login", this.loginForm)调用的Login也需要修改一下,在user.js里面。
在这里插入图片描述

最终效果
在这里插入图片描述
点击【登录】按钮
在这里插入图片描述

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

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

相关文章

从游戏到营销:抽卡机小程序的多维度应用探索

在数字化时代&#xff0c;小程序作为一种轻量级、即用即走的应用形态&#xff0c;正逐步渗透到人们生活的方方面面。其中&#xff0c;抽卡机小程序以其独特的趣味性和互动性&#xff0c;不仅在游戏领域大放异彩&#xff0c;更在营销领域展现出广阔的应用前景。本文将从游戏起源…

ELFK简介

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

Hadoop-11-MapReduce JOIN 操作的Java实现 Driver Mapper Reducer具体实现逻辑 模拟SQL进行联表操作

章节内容 上一节我们完成了&#xff1a; MapReduce的介绍Hadoop序列化介绍Mapper编写规范Reducer编写规范Driver编写规范WordCount功能开发WordCount本地测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学…

文件扫描pdf怎么弄?5个简易高效的文件扫描方法

在繁忙的工作中&#xff0c;我们常常需要将纸质文件快速转换为电子文档&#xff0c;以便于编辑、存储或分享。 无论是合同、报告还是笔记&#xff0c;将这些纸质文件转换为Word格式&#xff0c;不仅能提高工作效率&#xff0c;还能确保信息的安全备份。然而&#xff0c;面对市…

Redis 7.x 系列【16】持久化机制之 AOF

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 执行原理2.1 Redis 6.x2.1.1 直接写2.1.2 重写 2.2 Redis 7.x2.2.1 Redis 6…

Spring Ioc学习

第二章 Spring IOC 章节内容 Spring IOC技术实现Spring IOC设值注入Spring IOC构造注入 章节目标 掌握Spring IOC技术实现掌握Spring IOC设置注入掌握Spring IOC构造注入 第一节 Spring简介 1. Spring 简介 Spring 是目前主流的 Java 开发框架&#xff0c;是 Java 世界最…

基于Springboot+Vue+mysql仓库管理系统仓库进销存管理系统

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

【Python】Python中的数据类型

数据类型 导读一、数据类型的分类1.1 Number&#xff08;数字&#xff09;1.1.1 静态数据类型1.1.2 动态数据类型 1.2 String&#xff08;字符串&#xff09;1.3 bool&#xff08;布尔类型&#xff09; 二、数据类型的转换2.1 隐式类型转换2.2 显式类型转换2.2.1 int(x[,base])…

系统运维面试总结(shell编程)

SYNDDOS攻击&#xff0c;需要判断这个访问是正常访问还是信包攻击&#xff0c;当前这个信包发起的访问数量是多少&#xff0c;例如看到30个信包同时再访问时设置监控报警。 一般选用/dev/urandom生成&#xff0c;但其生成的随机数带有二进制乱码&#xff0c;所以需要tr命令…

CASS中按指定距离和方向移动图形

1、绘制一个图形 打开软件&#xff0c;随意绘制一个矩形&#xff0c;并量取左下角点的坐标值&#xff0c;具体如下&#xff1a; 2、按距离移动原理讲解 例1&#xff1a;将图形沿着y轴负方向移动100米&#xff0c;如何实现&#xff1f; 如上图所示&#xff0c;测绘中的坐标系…

多载波调制与OFDM原理讲解以及MATLAB实现GUI设计

前言 基于MATLAB设计并实现了一个OFDM调制的图形用户界面&#xff08;GUI&#xff09;系统。该系统旨在简化OFDM调制过程的仿真&#xff0c;提供友好的用户交互界面。设计目标是通过GUI实现参数化的OFDM仿真&#xff0c;包括子载波数、符号数、IFFT长度、循环前缀长度、循环后…

模拟退火算法2—优缺点

模拟退火算法优点 1、以一定的概率接受恶化解 模拟退火算法(SA)在搜索策略上与传统的随机搜索方法不同,它不仅引入了适当的随机因素,而且还引入了物理系统退火过程的自然机理。这种自然机理的引入使模拟退火算法在迭代过程中不仅接受使目标函数变“好”的试探点,而且还能以一…

【单片机毕业设计选题24034】-基于STM32的手机智能充电系统

系统功能: 系统可以设置充电时长&#xff0c;启动充电后按设置的充电时长充电&#xff0c;充电时间到后自动 停止充电&#xff0c;中途检测到温度过高也会结束充电并开启风扇和蜂鸣器报警。 系统上电后&#xff0c;OLED显示“欢迎使用智能充电系统请稍后”&#xff0c;两秒钟…

哨兵1SAR空间数据包协议数据单元文档(五)

《哨兵1SAR空间数据包协议数据单元》文档对数据包的结构进行了详细描述&#xff0c;并提供了用户数据的格式和解码算法。 原文链接: 哨兵1SAR空间数据包协议数据单元文档英文版 同系列中的其他文章篇链接: 哨兵1SAR空间数据包协议数据单元文档&#xff08;一&#xff09; 哨兵1…

保存在FinalShell服务器登录密码忘记了,如何快速获取到

一、从FinalShell获取服务器基本信息 如图操作会导出一个json文件&#xff0c;可以直接保存在桌面&#xff0c;或者其他位置 json格式如下&#xff1a; {"forwarding_auto_reconnect":false ,"custom_size":false ,"delete_time":0 ,"sec…

Python数据分析-旧金山犯罪预测分析(San Francisco Crime Classification)

一、研究背景 旧金山是一个人口稠密、旅游业发达的城市&#xff0c;同时也是美国犯罪率较高的城市之一。随着城市的不断发展&#xff0c;犯罪行为的类型和频率也在不断变化&#xff0c;这对城市的治安管理和社会稳定构成了巨大的挑战。近年来&#xff0c;数据科学技术的迅猛发…

C# 编程中互斥锁的使用

C# 中的互斥锁 互斥锁是 C# 中使用的同步原语&#xff0c;用于控制多个线程或进程对共享资源的访问。其目的是确保在任何给定时间只有一个线程或进程可以获取互斥锁&#xff0c;从而提供互斥。 C# 中互斥锁的优点 可以使用互斥锁 (Mutex) 并享受其带来的好处。 1. 共享资源…

德国威步的技术演进之路(下):从云端许可管理到硬件加密狗的创新

从单机用户许可证到WkNET网络浮点授权的推出&#xff0c;再到引入使用次数和丰富的时间许可证管理&#xff0c;德国威步产品不断满足市场对灵活性和可扩展性的需求。TCP/IP浮动网络许可证进一步展示了威步技术在网络时代的创新应用。借助于2009年推出的借用许可证以及2015年推出…

mac磁盘工具如何合并分区 macos 磁盘工具 无法抹除 磁盘管理软件哪个使用率最高

一、什么是NTFS格式分区 NTFS格式分区是微软公司开发的诸多文件系统中的一种。NTFS格式分区是一种文件系统&#xff0c;磁盘只有在安装了文件系统后才能被正常使用&#xff0c;文件系统的格式有非常多&#xff0c;常见的有FAT 32和NTFS。 作为常见文件系统&#xff0c;NTFS格式…

无人机集群协同搜索研究综述

源自&#xff1a;指挥控制与仿真 作者&#xff1a;刘圣洋, 宋婷, 冯浩龙, 孙玥, 韩飞 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 摘要 无人机集群协同区域搜索能够有效地获取任务区域地面信息,降低环境不确定度。基…