【SpringBoot】SpringBoot使用mail实现登录邮箱验证

427ef4152dbf4b6c92618a198935cb6c.png

  📝个人主页:哈__

期待您的关注 

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、前期准备

1 开启邮箱服务

2 SpringBoot导入依赖

3 创建application.yml配置文件 

4 创建数据库文件

 5 配置redis服务

二、验证邮件发送功能 

三、注册功能实现邮箱验证

1 创建User实体类

2 创建UserParam

3 创建CaptchaService

4 创建EmailTemplateEnum

5 创建CaptchaServiceImpl

 6 创建CaptchaController

7 创建LoginController 

四、创建登陆页面 

1 login.html 

2 register.html


 

在实际的开发当中,不少的场景中需要我们使用更加安全的认证方式,同时也为了防止一些用户恶意注册,我们可能会需要用户使用一些可以证明个人身份的注册方式,如短信验证、邮箱验证等。

一、前期准备

为了实现邮箱认证服务,我们需要提供出来一个邮箱作为验证码的发送者,这里我使用的是QQ邮箱。

1 开启邮箱服务

首先打开QQ邮箱,然后找到设置,点击账号。

a5b0567d19f143a7acbdfa6c60ff09f0.png

然后找到下方的邮件协议服务,打开。因为这里我已经打开了,而且服务开启也较为简单,需要我们发送一个短信到指定的号码,可能会收取一定的费用,不过不是很多,好像是几毛钱。

31d8524967e54c389103fe92393c8b86.png 开启之后会给你一个授权码,一定要记住这个授权码,发邮件的时候需要这个。

2 SpringBoot导入依赖

核心的就是mail依赖,因为我这个项目东西不少,为了方便我就全拷贝过来了,可能有的用不到。

 <properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.7.6</spring-boot.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><mybatisplus.version>3.4.3.1</mybatisplus.version><mybatisplus.generator>3.3.2</mybatisplus.generator><mybatisplus.velocity>1.7</mybatisplus.velocity></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatisplus.generator}</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>${mybatisplus.velocity}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency></dependencies>

3 创建application.yml配置文件 

spring:#邮件服务配置mail:host: smtp.qq.com #邮件服务器地址protocol: smtp #协议username:  #发送邮件的邮箱也就是你开通服务的邮箱password:  #开通服务后得到的授权码default-encoding: utf-8 #邮件内容的编码redis:host: 127.0.0.1port: 6379datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mailusername: rootpassword:  # 数据库的密码

4 创建数据库文件

在上边的配置文件中你也看到了,我们用到了mysql还有redis。

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` int(11) NOT NULL AUTO_INCREMENT,`account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', '123456');
INSERT INTO `user` VALUES (2, '123456', '123456');SET FOREIGN_KEY_CHECKS = 1;

 5 配置redis服务

在之前的文章当中我有说到过安装redis的事情,大家可以看看我这篇文章。【Spring】SpringBoot整合Redis,用Redis实现限流(附Redis解压包)_springboot 限流 redis-CSDN博客

二、验证邮件发送功能 

大家可以先看一下我的项目结构。其中的一些代码是我学习另一位大佬的文章,这篇文章也是对该大佬文章的一个总结。蒾酒-CSDN博客

d18a1b016999488794cb4594e46ab934.png

我们最重要的邮件发送工具 就是util包下的EmailApi。将以下代码导入后,创建一个测试方法。

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Objects;/*** @author mijiupro*/
@Component
@Slf4j
public class EmailApi {@Resourceprivate JavaMailSender mailSender;@Value("${spring.mail.username}")private String from ;// 发件人/*** 发送纯文本的邮件* @param to 收件人* @param subject 主题* @param content 内容* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendGeneralEmail(String subject, String content, String... to){// 创建邮件消息SimpleMailMessage message = new SimpleMailMessage();message.setFrom(from);// 设置收件人message.setTo(to);// 设置邮件主题message.setSubject(subject);// 设置邮件内容message.setText(content);// 发送邮件mailSender.send(message);return true;}/*** 发送html的邮件* @param to 收件人* @param subject 主题* @param content 内容* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendHtmlEmail(String subject, String content, String... to){// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);// 设置邮件内容helper.setText(content, true);// 发送邮件mailSender.send(mimeMessage);log.info("发送邮件成功");return true;}/*** 发送带附件的邮件* @param to 收件人* @param subject 主题* @param content 内容* @param filePaths 附件路径* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendAttachmentsEmail(String subject, String content, String[] to, String[] filePaths) {// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);// 设置邮件内容helper.setText(content,true);// 添加附件if (filePaths != null) {for (String filePath : filePaths) {FileSystemResource file = new FileSystemResource(new File(filePath));helper.addAttachment(Objects.requireNonNull(file.getFilename()), file);}}// 发送邮件mailSender.send(mimeMessage);return true;}/*** 发送带静态资源的邮件* @param to 收件人* @param subject 主题* @param content 内容* @param rscPath 静态资源路径* @param rscId 静态资源id* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendInlineResourceEmail(String subject, String content, String to, String rscPath, String rscId) {// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);// 设置发件人helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);//html内容图片String contentHtml = "<html><body>这是邮件的内容,包含一个图片:<img src=\'cid:" + rscId + "\'>"+content+"</body></html>";helper.setText(contentHtml, true);//指定讲资源地址FileSystemResource res = new FileSystemResource(new File(rscPath));helper.addInline(rscId, res);mailSender.send(mimeMessage);return true;}}

 进行邮件发送的测试。

cce23808aa9c476eb51c0896c3ef38fa.png

 看看结果,成功的发过来了。

e582910586124e76b8b1baf429574f72.png

 接下来就要进行登录注册功能的开发了。

三、注册功能实现邮箱验证

1 创建User实体类

@Data
@EqualsAndHashCode(callSuper = false)
public class User implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String account;private String password;}

2 创建UserParam

@Data
@EqualsAndHashCode(callSuper = false)
public class UserParam implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String account;private String password;private String emailCode;private String email;}

3 创建CaptchaService

public interface CaptchaService {boolean sendCaptcha(String email);
}

4 创建EmailTemplateEnum

这一步我没有选择创建CaptchaServiceImpl,因为这个类中涉及到了一些核心的代码,而我们一些类还没有创建完,我们先创建这样的一个枚举类,这个枚举类的作用就是定义我们发送邮件的一个模板,我们在发送邮件的时候,直接向模板内插入内容就可以了。

ublic enum EmailTemplateEnum {// 验证码邮件VERIFICATION_CODE_EMAIL_HTML("<html><body>用户你好,你的验证码是:<h1>%s</h1>请在五分钟内完成注册</body></html>","登录验证"),// 用户被封禁邮件通知USER_BANNED_EMAIL("用户你好,你已经被管理员封禁,封禁原因:%s", "封禁通知");private final String template;private final String subject;EmailTemplateEnum(String template, String subject) {this.template = template;this.subject = subject;}public String getTemplate(){return this.template;}public String set(String captcha) {return String.format(this.template, captcha);}public String getSubject() {return this.subject;}
}

5 创建CaptchaServiceImpl

首先把我们前端传过来的邮箱加一个前缀,用于redis中的存储,因为我们不仅可以有邮箱认证还可以有手机认证。

 @ResourceStringRedisTemplate stringRedisTemplate;@ResourceEmailApi emailApi;@Overridepublic boolean sendCaptcha(String email) {sendMailCaptcha("login:email:captcha:"+email);return true;}

 在redis当中,验证码的存储使用的是Hash结构,Hash存储了验证码,验证次数,还有上一次的发送时间,因为我们要限制一分钟发送的次数。一分钟内我们只能发一条短信,验证码在redis中的过期时间为五分钟,在验证码未过期之前发送的认证,都会让这个发送次数加一,倘若发送的次数达到了5次还要发送,那么就封禁一天不让发送短信。

例如,在3:30:30的时候发送了一次短信,一分钟后,3:31:30的时候又发送了短信,直到3:35:30的时候又发了一次,此时的发送次数已经达到了5,这时候就会封一天,因为每次发送验证码的时候,redis都存储着上一次还没过期的验证码,所以发送次数会增加。

下边讲一下代码。

先从redis中找到这个hash结构,如果hash结构的值不为空并且达到了发送次数上限,就封禁一天,否则的话看一下上一次的发送时间是否存在,如果存在的话,判断一下当前时间和上一次的发送时间间隔是否大于60秒,如果小于60秒那么不让发送。如果正常发送短信,那么就把发送的次数加1,然后用随机数生成一个六位数的验证码,发送验证码,并且向redis中保存刚才的hash结构。

  private boolean sendMailCaptcha(String key){BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(key);// 初始检查String lastSendTimestamp = hashOps.get("lastSendTimestamp");String sendCount = hashOps.get("sendCount");if(StringUtils.isNotBlank(sendCount)&&Integer.parseInt(sendCount)>=5){hashOps.expire(24, TimeUnit.HOURS);throw new  RuntimeException("验证码发送过于频繁");}if(StringUtils.isNotBlank(lastSendTimestamp)){long lastSendTime = Long.parseLong(lastSendTimestamp);long currentTime = System.currentTimeMillis();long elapsedTime = currentTime - lastSendTime;if(elapsedTime < 60 * 1000){throw new  RuntimeException("验证码发送过于频繁");}}int newSendCount = StringUtils.isNotBlank(sendCount) ? Integer.parseInt(sendCount) + 1 : 1;String captcha = RandomStringUtils.randomNumeric(6);try {sendCaptcha(key,captcha);} catch (Exception e) {return false;}hashOps.put("captcha", captcha);hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));hashOps.put("sendCount", String.valueOf(newSendCount));hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟return true;}

发送验证码调用的是下边的函数。

 private void sendCaptcha(String hashKey, String captcha) thorw Exception{// 根据hashKey判断是发送邮件还是短信,然后调用相应的发送方法if("email".equals(hashKey.split(":")[1])){if (!emailApi.sendHtmlEmail(EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.getSubject(),EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.set(captcha),hashKey.split(":")[3])) {throw new RuntimeException("发送邮件失败");}}}

 6 创建CaptchaController

@RestController
@RequestMapping("/captcha")
public class CaptchaController {@ResourceCaptchaService captchaService;@ResourceEmailApi emailApi;@RequestMapping("/getCaptcha")public Result sendCaptcha(String email){boolean res = captchaService.sendCaptcha(email);if(res){return new Result("发送成功",200,null);}return new Result("发送失败",500,null);}
}

7 创建LoginController 

UserSevice的东西都很简单,都是mybatisplus的内容,如果不太了解可以看我这篇文章【Spring】SpringBoot整合MybatisPlus的基本应用_简单的springboot+mybatisplus的应用程序-CSDN博客

我这里并没有用UserService封装认证的过程,直接写到controller中了,大家能看懂就好。仅供学习使用。 

@Controller
public class LoginController {@AutowiredUserService userService;@AutowiredStringRedisTemplate stringRedisTemplate;@GetMapping("/")public String Login(){return "redirect:/pages/login.html";}@RequestMapping("/register")@ResponseBodypublic Result registerUser( UserParam user ){String email = user.getEmail();String emailCode = user.getEmailCode();BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps("login:email:captcha:"+email);String code = hashOps.get("captcha");if(!Objects.equals(code, emailCode)){return new Result("验证码错误",400,null);}User user1 = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getAccount,user.getAccount()));//如果有这个用户的信息要拒绝注册if(user1!=null){return new Result("",100,null);}User saveUser = new User();BeanUtils.copyProperties(user,saveUser);System.out.println(user);System.out.println(saveUser);boolean save = userService.save(saveUser);if(!save){return new Result("注册失败",300,null);}return new Result("注册成功",200,null);}
}

到此为止,验证码的注册功能就已经实现完成了。

我们现在要做一个前端页面。

四、创建登陆页面 

在resources目录下创建static文件夹。在pages目录下添加login.html和register.html。至于jquery呢就要大家自己去找了。

6a71f75baaa54fae8b7c0bd5ab3e8d86.png

 

1 login.html 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../js/jquery.min.js"></script><script>$(function() {$(".btn").on("click", function () {var account = $(".account").val()var password = $(".password").val()console.log(account)console.log(password)$.ajax({type: "get",url: "/login?&account=" + account + "&password=" + password,success: function (data) {if (data.code === 200) {window.location.href = "../pages/index.html"}else{alert("登陆失败,请检查账号或者密码")}}})})});</script><style>*{padding: 0;margin: 0px;text-decoration: none;}body {background-image: url(../images2/1.jpg);background-size:cover;background-attachment:fixed;}.loginbox {height: 280px;width: 26%;margin: 340px auto;background-color: rgb(158, 151, 158);opacity: 0.7;text-align: center;}.loginbox .top {display: block;height: 42px;background: linear-gradient(90deg,pink,red,pink,yellow,red);padding-top: 15px;font-size: 20px;font-weight: 600;color: black;}.loginbox span {color: black;font-weight: 500;font-size: 18px;}.loginbox input[type=text],.loginbox input[type=password] {height: 20px;border: none;border-radius: 4px;outline: none;}.loginbox input[type=button] {width: 40%;height: 24px;border: none;border-radius: 4px;margin: 0 auto;outline: none;}.loginbox .a {display: flex;margin: 20px auto;width: 45%;justify-content: space-between;}.loginbox .a a {color: white;}.loginbox .a a:hover {color: rgb(228, 221, 228);}</style>
</head>
<body><div class="loginbox"><span class="top">邮箱简易发送系统</span><br><span>账号:</span><input type="text" placeholder="请输入账号" class="account"><br><br><span>密码:</span><input type="password" placeholder="请输入密码" class="password"><br><br><input type="button" value="登录" class="btn"><div class="a"><a href="register.html">注册</a><a href="">忘记密码</a></div></div>
</body>
</html>

2 register.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="../css/register.css"><script src="../js/jquery.min.js"></script><script>$(function(){// 注册按钮点击事件$(".btn").on("click",function(){var account = $(".account").val().trim();var password = $(".password").val().trim();var emailCode = $(".emailCode").val().trim();var email = $(".email").val().trim();$.ajax({type:"post",url:"/register",data:{"account":account,"password":password,"emailCode":emailCode,"email":email},success:function(data){if(data.code=== 100){alert("该账号已存在");}else if(data.code === 300){alert("账号注册失败");}else if(data.code === 400){alert("验证码错误");}else{alert("账号注册成功");window.location.href="../pages/login.html";}}});});// 发送验证码按钮点击事件$(".sendCode").on("click", function(){var email = $(".email").val().trim();if(email === "") {alert("请输入邮箱");return;}// 发送验证码请求$.ajax({type:"get",url:"/captcha/getCaptcha?email="+email,success:function(data){console.log(data)if(data.code === 200){alert("验证码已发送到邮箱");// 启动倒计时startCountdown(60);} else {alert("验证码发送失败,请重试");}}});});// 启动倒计时函数function startCountdown(seconds) {var timer = seconds;$(".sendCode").prop("disabled", true);var countdown = setInterval(function() {$(".sendCode").val(timer + "秒后可重新发送");timer--;if (timer < 0) {clearInterval(countdown);$(".sendCode").prop("disabled", false);$(".sendCode").val("发送验证码");}}, 1000);}});</script><style>*{padding: 0;margin: 0px;text-decoration: none;}body {background-image: url(../images2/1.jpg);background-size: cover;background-attachment: fixed;}.loginbox {height: 380px; /* 调整高度以适应新字段 */width: 26%;margin: 340px auto;background-color: rgb(158, 151, 158);opacity: 0.7;text-align: center;}.loginbox .top {display: block;height: 42px;background: linear-gradient(90deg, pink, red, pink, yellow, red);padding-top: 15px;font-size: 20px;font-weight: 600;color: black;}.loginbox span {color: black;font-weight: 500;font-size: 18px;}.loginbox input[type=text],.loginbox input[type=password] {height: 20px;border: none;border-radius: 4px;outline: none;}.loginbox input[type=button] {width: 40%;height: 24px;border: none;border-radius: 4px;margin: 0 auto;outline: none;}.loginbox .a {display: flex;margin: 20px auto;width: 45%;justify-content: space-between;}.loginbox .a a {color: white;}.loginbox .a a:hover {color: rgb(228, 221, 228);}</style>
</head>
<body>
<div class="loginbox"><span class="top">邮箱简易发送系统</span><br><span>账号:</span><input type="text" placeholder="请输入账号" class="account"><br><br><span>密码:</span><input type="password" placeholder="请输入密码" class="password"><br><br><span>邮箱:</span><input type="text" placeholder="请输入邮箱" class="email"><br><br><input type="button" value="发送验证码" class="sendCode"><br><br><span>验证码:</span><input type="text" placeholder="请输入验证码" class="emailCode"><br><br><input type="button" value="注册" class="btn"><div class="a"><a href="login.html">返回</a></div>
</div>
</body>
</html>

2625f728ad30493391e9248fc41e6579.png

 

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

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

相关文章

昆虫学(书籍学习资料)

包括昆虫分类&#xff08;上下册&#xff09;、昆虫生态大图鉴等书籍资料。

APKDeepLens:一款针对Android应用程序的安全扫描工具

关于APKDeepLens APKDeepLens是一款针对Android应用程序的安全扫描工具&#xff0c;该工具基于Python开发&#xff0c;旨在扫描和识别Android应用程序&#xff08;APK文件&#xff09;中的安全漏洞。 APKDeepLens主要针对的是OWASP Top 10移动端安全漏洞&#xff0c;并为开发人…

[Microsoft Office]Word设置页码从第二页开始为1

目录 第一步&#xff1a;设置页码格式 第二步&#xff1a;设置“起始页码”为0 第三步&#xff1a;双击页码&#xff0c;出现“页脚”提示 第四步&#xff1a;选中“首页不同” 第一步&#xff1a;设置页码格式 第二步&#xff1a;设置“起始页码”为0 第三步&#xff1a;双…

与Flat Ads相约ChinaJoy 2024,共探全球化增长

在当今全球数字化浪潮的推动下,游戏产业作为文化与技术融合的先锋,正以前所未有的速度跨越国界,开启全球化发展的新篇章。随着第二十一届ChinaJoy的临近,全球的目光再次聚焦于上海新国际博览中心,这里即将成为数字娱乐与科技创新碰撞与交融的璀璨舞台。 而在这场盛会上,Flat A…

四川赤橙宏海商务信息咨询有限公司引领抖音电商浪潮

在数字时代的浪潮下&#xff0c;电商行业飞速发展&#xff0c;抖音电商作为新兴的电商模式&#xff0c;凭借其独特的社交属性和短视频形式&#xff0c;迅速吸引了众多消费者和商家的目光。四川赤橙宏海商务信息咨询有限公司&#xff0c;作为抖音电商服务的佼佼者&#xff0c;凭…

[深度学习]卷积理解

单通道卷积 看这个的可视化就很好理解了 https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md 多通道卷积 当输入有多个通道时,卷积核需要拥有相同的通道数. 假设输入有c个通道,那么卷积核的每个通道分别于相应的输入数据通道进行卷积,然后将得到的特征图对…

Ubuntu / Debian安装FTP服务

本章教程,记录在Ubuntu中安装FTP服务的具体步骤。FTP默认端口:21 1、安装 pure-ftpd sudo apt-get install pure-ftpd2、修改默认配置 # 与 centos 不同,这里需要在 /etc/pure-ftpd/conf 文件夹下执行下列命令,增加对应配置文件: # 创建 /etc/pure-ftpd/conf/PureDB 文件…

Load Tensor to local Nvidia GPU

0. 安装Nvidia驱动 ubuntu24.04的安装非常简单&#xff0c;在安装界面&#xff0c;选择为"图形化和其他硬件安装驱动"&#xff0c;重启后即有原版Nvidia驱动(如图Nvidia X xxx) 1.确定电脑上是否有NvidiaGPU且安装好Nvidia驱动 import torch print(torch.version…

Rocky Linux 9.4基于官方源码制作openssh 9.8p1二进制rpm包 —— 筑梦之路

2024年7月1日&#xff0c;openssh 9.8版本发布&#xff0c;主要修复了CVE-2024-6387安全漏洞。 由于centos 7的生命周期在6月30日终止&#xff0c;因此需要逐步替换到Rocky Linux&#xff0c;后续会有更多分享关于Rocky Linux的文章。 环境说明 1. 操作系统版本 cat /etc/o…

2024攻防演练:亚信安全新一代WAF,关键时刻守护先锋

实网攻防 网络安全如同一面坚固的盾牌&#xff0c;保护着我们的信息资产免受无孔不入的威胁。而其中&#xff0c;WAF就像网络安全的守门员&#xff0c;关键时刻挺身而出&#xff0c;为您的企业筑起一道坚实的防线。 攻防不对等 防守方实时应答压力山大 在攻防对抗中&#xf…

通信安全员考试精选练习题库,2024年备考必刷题!

16.设计单位必须在设计文件中&#xff08;&#xff09;计列安全生产费。 A.全额 B.部分 C.按建设单位要求 D.按工程建设需要 答案&#xff1a;A 17.日最高气温达到&#xff08;&#xff09;℃以上&#xff0c;应当停止当日室外露天作业。 A.38 B.36 C.35 D.40 答案&…

【C++】日期类

鼠鼠实现了一个日期类&#xff0c;用来练习印证前几篇博客介绍的内容&#xff01;&#xff01; 目录 1.日期类的定义 2.得到某年某月的天数 3.检查日期是否合法 4.&#xff08;全缺省&#xff09;构造函数 5.拷贝构造函数 6.析构函数 7.赋值运算符重载 8.>运算符重…

有人物联的串口服务器USR-TCP232-410S基本测试通信和使用方案(485串口和232串口)

1.将 410S(USR-TCP232-410S&#xff0c;简称 410S 下同)的串口通过串口线(或USB 转串口线)与计算机相连接&#xff0c;通过网线将 410S 的网口 PC 的网口相连接&#xff0c;检测硬件连接无错误后&#xff0c;接入我们配送的电源适配器&#xff0c;给 410S 供电。观察指示灯状态…

jmeter-beanshell学习2-beanshell断言

继续写&#xff0c;之前写了获取变量&#xff0c;设置变量&#xff0c;今天先写个简单点的断言。 一般情况用响应断言&#xff0c;就挺好使&#xff0c;但是自动化还要生成报告&#xff0c;如果断言失败了&#xff0c;要保存结果&#xff0c;只能用beanshell处理&#xff0c;顺…

智慧矿山:EasyCVR助力矿井视频多业务融合及视频转发服务建设

一、方案背景 随着矿井安全生产要求的不断提高&#xff0c;视频监控、数据传输、通讯联络等业务的需求日益增长。为满足矿井生产管理的多元化需求&#xff0c;提高矿井作业的安全性和效率&#xff0c;TSINGSEE青犀EasyCVR视频汇聚/安防监控综合管理平台&#xff0c;旨在构建一…

【代码随想录】【算法训练营】【第55天】 [42]接雨水 [84]柱状图中最大的矩形

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 55&#xff0c;又是一个周一&#xff0c;不能再坚持~ 题目详情 [42] 接雨水 题目描述 42 接雨水 解题思路 前提&#xff1a;雨水形成的情况是凹的, 需要前中后3个元素&#xff0c;计算该元…

java集合(1)

目录 一.集合概述 二. 集合体系概述 1. Collection接口 1.1 List接口 1.2 Set接口 2. Map接口 三. ArrayList 1.ArrayList常用方法 2.ArrayList遍历 2.1 for循环 2.2 增强for循环 2.3 迭代器遍历 一.集合概述 我们经常需要存储一些数据类型相同的元素,之前我们学过…

MySQL-核心知识要点

1、索引的数据结构-Btree BTree的优势&#xff1a; B树的内节点无data&#xff0c;一个节点可以存储更多的K-V对。在构造树时&#xff0c;需要的内节点会更少&#xff0c;那么树的层级也会越低。查询一条数据时&#xff0c;1. 扫描的层级低&#xff0c;扫描过的节点更少&…

端口被占用的解决办法、netstat命令;Linux ps命令详解,Linux查看进程

文章目录 一、端口被占用的原因二、端口被占用的解决方法2.1 Windows系统2.2 Linux系统 三、Linux命令补充3.1 Linux查看端口占用情况3.2 netstat命令详解3.3 ps命令3.3.1 常用命令3.3.2 拓展命令3.3.3 字段补充 运行软件或程序时&#xff0c;有时会出现以下问题、导致运行失败…

Matlab进阶绘图第62期—滑珠气泡图

在之前的文章中分享了滑珠散点图的绘制方法&#xff1a; 在此基础上&#xff0c;添加尺寸参数&#xff0c;通过散点的大小表示一个额外的特征&#xff0c;便是滑珠气泡图。 由于Matlab中没有现成的函数绘制滑珠气泡图&#xff0c;因此需要大家自行解决。 本文利用自己制作的B…