GoogleCodeUtil.java

news2025/7/16 9:59:46

Google动态验证码实现  GoogleCodeUtil.java

package zwf;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;


/**
https://mvnrepository.com/artifact/commons-codec/commons-codec/1.18.0

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.18.0</version>
</dependency>
 */
import org.apache.commons.codec.binary.Base32;


/**
https://mvnrepository.com/artifact/com.warrenstrange/googleauth/1.5.0

<!-- https://mvnrepository.com/artifact/com.warrenstrange/googleauth -->
<dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId>googleauth</artifactId>
    <version>1.5.0</version>
</dependency>
 */
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;

/**
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/client/utils/URIBuilder
	at com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(GoogleAuthenticatorQRGenerator.java:168)
	at com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator.getOtpAuthURL(GoogleAuthenticatorQRGenerator.java:143)
	at zwf.GoogleAuthenticatorUtil.generateQRUrl(GoogleAuthenticatorUtil.java:92)
	at zwf.GoogleAuthenticatorUtil.main(GoogleAuthenticatorUtil.java:163)
Caused by: java.lang.ClassNotFoundException: org.apache.http.client.utils.URIBuilder
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	... 4 more

https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.5.13

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
 */

/**
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/message/ParserCursor
	at org.apache.http.client.utils.URLEncodedUtils.splitSegments(URLEncodedUtils.java:320)
	at org.apache.http.client.utils.URLEncodedUtils.splitPathSegments(URLEncodedUtils.java:348)
	at org.apache.http.client.utils.URIBuilder.setPath(URIBuilder.java:293)
	at com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(GoogleAuthenticatorQRGenerator.java:171)
	at com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator.getOtpAuthURL(GoogleAuthenticatorQRGenerator.java:143)
	at zwf.GoogleAuthenticatorUtil.generateQRUrl(GoogleAuthenticatorUtil.java:108)
	at zwf.GoogleAuthenticatorUtil.main(GoogleAuthenticatorUtil.java:179)
Caused by: java.lang.ClassNotFoundException: org.apache.http.message.ParserCursor
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	... 7 more

https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore/4.4.15
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.4.15</version>
</dependency>
 */

/**
 * Google动态验证码实现
 * 
 * https://www.lzltool.com/GoogleDynamicPassword
 * 
 * @author ZengWenFeng
 * @date 2025.04.16
 * @mobile 13805029595
 * @email 117791303@qq.com
 */
public class GoogleCodeUtil
{

	/**
	 * 动态密码的有效期,默认30秒, 30 * 1000 = 10秒
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @return
	 */
	private static final long TIME_STEP_SIZE_IN_MILLIS = 30 * 1000;

	/**
	 * 允许的时间窗口时间步长,默认1
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @return
	 */
	private static final int WINDOW_SIZE = 1;

	/**
	 * 生成一个随机的 Base32 编码的密钥
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @return 生成的 Base32 编码的密钥
	 */
	public static String generateSecretKey()
	{
		// 创建一个安全的随机数生成器
		SecureRandom random = new SecureRandom();

		// 生成一个长度为 20 的字节数组
		byte[] bytes = new byte[20];

		// 用随机数填充字节数组
		random.nextBytes(bytes);

		// 创建一个 Base32 编码器
		Base32 base32 = new Base32();

		// 将字节数组编码为 Base32 字符串并返回
		return base32.encodeToString(bytes);
	}

	/**
	 * 根据密钥生成一个可供 Google Authenticator 扫描的二维码链接
	 * 
	 * https://api.qrserver.com/v1/create-qr-code/?data=otpauth://totp/YourAppName:user@example.com?secret=YOUR_SECRET_KEY&issuer=YourAppName
	 * 
	 * url转义
	 * :     %3A
	 * //    %2F%2F
	 * @     %40
	 * ?     %3F
	 * =     %3D
	 * &     %26
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于生成二维码链接的密钥
	 * @param appName              应用程序的名称
	 * @param email                用户的邮箱地址
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @param windowSize           允许的时间窗口时间步长
	 * @return 生成的二维码链接
	 */
	public static String generateQRUrl(String secretKey, String appName, String email, long timeStepSizeInMillis,
			int windowSize)
	{
		// 构建 GoogleAuthenticator 的配置对象,设置时间步长
		GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
				.setTimeStepSizeInMillis(timeStepSizeInMillis).setWindowSize(windowSize) // 设置允许的时间窗口为 1 个时间步长
				.build();

		// 根据配置创建 GoogleAuthenticator 实例
		@SuppressWarnings("unused")
		GoogleAuthenticator gAuth = new GoogleAuthenticator(config);

		// 根据密钥创建一个 GoogleAuthenticatorKey 对象
		GoogleAuthenticatorKey key = new GoogleAuthenticatorKey.Builder(secretKey).build();

		// 生成一个可供 Google Authenticator 扫描的二维码链接,其中包含应用名称和用户邮箱信息
		String url = GoogleAuthenticatorQRGenerator.getOtpAuthURL(appName, email, key);

		// 对 URL 进行编码
		try
		{
			return URLEncoder.encode(url, StandardCharsets.UTF_8.name());
		}
		catch (UnsupportedEncodingException e)
		{
			// 这里可以考虑记录日志,目前简单打印异常信息
			System.err.println("URL 编码时出现异常: " + e.getMessage());
			// 返回 null 表示生成失败,调用者可以根据返回值进行相应处理
			return null;
		}
	}

	/**
	 * 根据密钥生成一个可供 Google Authenticator 扫描的二维码链接
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于生成二维码链接的密钥
	 * @param appName              应用程序的名称
	 * @param email                用户的邮箱地址
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @return 生成的二维码链接
	 */
	public static String generateQRUrl(String secretKey, String appName, String email, long timeStepSizeInMillis)
	{
		// 生成一个可供 Google Authenticator 扫描的二维码链接,其中包含应用名称和用户邮箱信息
		return generateQRUrl(secretKey, appName, email, timeStepSizeInMillis, WINDOW_SIZE);
	}

	/**
	 * 根据密钥生成一个可供 Google Authenticator 扫描的二维码链接
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey 用于生成二维码链接的密钥
	 * @param appName   应用程序的名称
	 * @param email     用户的邮箱地址
	 * @return 生成的二维码链接
	 */
	public static String generateQRUrl(String secretKey, String appName, String email)
	{
		// 生成一个可供 Google Authenticator 扫描的二维码链接,其中包含应用名称和用户邮箱信息
		return generateQRUrl(secretKey, appName, email, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
	}

	/**
	 * 根据密钥生成当前时间对应的一次性动态密码
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于生成动态密码的密钥
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @param windowSize           允许的时间窗口时间步长
	 * @return 当前时间对应的一次性动态密码
	 */
	public static int generateCode(String secretKey, long timeStepSizeInMillis, int windowSize)
	{
		// 构建 GoogleAuthenticator 的配置对象,设置时间步长
		GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
				.setTimeStepSizeInMillis(timeStepSizeInMillis).setWindowSize(windowSize) // 设置允许的时间窗口为 1 个时间步长
				.build();

		// 根据配置创建 GoogleAuthenticator 实例
		GoogleAuthenticator gAuth = new GoogleAuthenticator(config);

		// 利用密钥生成当前时间对应的一次性动态密码
		return gAuth.getTotpPassword(secretKey);
	}

	/**
	 * 根据密钥生成当前时间对应的一次性动态密码
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于生成动态密码的密钥
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @return 当前时间对应的一次性动态密码
	 */
	public static int generateCode(String secretKey, long timeStepSizeInMillis)
	{
		// 利用密钥生成当前时间对应的一次性动态密码
		return generateCode(secretKey, timeStepSizeInMillis, WINDOW_SIZE);
	}

	/**
	 * 根据密钥生成当前时间对应的一次性动态密码
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey 用于生成动态密码的密钥
	 * @return 当前时间对应的一次性动态密码
	 */
	public static int generateCode(String secretKey)
	{
		// 利用密钥生成当前时间对应的一次性动态密码
		return generateCode(secretKey, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
	}

	/**
	 * 验证输入的动态密码是否与根据密钥生成的密码匹配
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于验证的密钥
	 * @param code                 待验证的动态密码
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @param windowSize           允许的时间窗口时间步长
	 * @return 若验证通过返回 true,否则返回 false
	 */
	public static boolean checkCode(String secretKey, int code, long timeStepSizeInMillis, int windowSize)
	{
		// 构建 GoogleAuthenticator 的配置对象,设置时间步长
		GoogleAuthenticatorConfig config = new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
				.setTimeStepSizeInMillis(timeStepSizeInMillis).setWindowSize(windowSize) // 设置允许的时间窗口为 1 个时间步长
				.build();

		// 根据配置创建 GoogleAuthenticator 实例
		GoogleAuthenticator gAuth = new GoogleAuthenticator(config);

		// 验证输入的动态密码是否与根据密钥生成的密码匹配
		return gAuth.authorize(secretKey, code);
	}

	/**
	 * 验证输入的动态密码是否与根据密钥生成的密码匹配
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey            用于验证的密钥
	 * @param code                 待验证的动态密码
	 * @param timeStepSizeInMillis 动态密码的时间步长(毫秒)
	 * @return 若验证通过返回 true,否则返回 false
	 */
	public static boolean verifyCode(String secretKey, int code, long timeStepSizeInMillis)
	{
		// 验证输入的动态密码是否与根据密钥生成的密码匹配
		return checkCode(secretKey, code, timeStepSizeInMillis, WINDOW_SIZE);
	}

	/**
	 * 验证输入的动态密码是否与根据密钥生成的密码匹配
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param secretKey 用于验证的密钥
	 * @param code      待验证的动态密码
	 * @return 若验证通过返回 true,否则返回 false
	 */
	public static boolean verifyCode(String secretKey, int code)
	{
		// 验证输入的动态密码是否与根据密钥生成的密码匹配
		return checkCode(secretKey, code, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
	}

	/**
	 * 程序入口,用于测试动态密码生成和验证功能
	 * 
	 * @author ZengWenFeng
	 * @date 2025.04.16
	 * @mobile 13805029595
	 * @email 117791303@qq.com
	 * @param args 命令行参数
	 */
	public static void main(String[] args)
	{
		//
		// 生成一个用于生成动态密码的密钥
		String secretKey = generateSecretKey();
		System.out.println("Key  : " + secretKey);

		String appName = "ZengWenFeng";
		String userEmail = "117791303@ZengWenFeng.com";

		// 根据生成的密钥生成一个可供 Google Authenticator 扫描的二维码链接
		String qrCodeUrl = generateQRUrl(secretKey, appName, userEmail, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
		System.out.println("Url  : " + qrCodeUrl);

		// 利用生成的密钥生成当前时间对应的一次性动态密码
		int code = generateCode(secretKey, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
		System.out.println("Code  : " + code);

		// 验证生成的动态密码是否有效
		boolean isValid = checkCode(secretKey, code, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
		System.out.println("Valid : " + isValid);

		/*
		
		Secret Key: SRT5EAIQICUOWEI7QVPQ2DQ4JMQUTV67 QR Code URL:
		https://api.qrserver.com/v1/create-qr-code/?data=otpauth%3A%2F%2Ftotp%2FZengWenFeng%3A117791303%40ZengWenFeng.com%3Fsecret%3DSRT5EAIQICUOWEI7QVPQ2DQ4JMQUTV67%26issuer%3DZengWenFeng%26algorithm%3DSHA1%26digits%3D6%26period%3D30&size=200x200&ecc=M&margin=0 
		
		Current Code: 883702   Is Valid Code: true
		
		 */

		try
		{
			// 等待超过有效期,这里设置为 TIME_STEP_SIZE_IN_MILLIS + 1毫秒
			System.out.println("---------------------------------");
			System.out.println("Waiting for the code to expire...");
			Thread.sleep(TIME_STEP_SIZE_IN_MILLIS + 1);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}

		// 再次验证过期的动态密码
		boolean isExpiredValid = checkCode(secretKey, code, TIME_STEP_SIZE_IN_MILLIS, WINDOW_SIZE);
		System.out.println("Valid (after expiration): " + isExpiredValid);
	}
}

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

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

相关文章

Maven 简介(图文)

Maven 简介 Maven 是一个Java 项目管理和构建的工具。可以定义项目结构、项目依赖&#xff0c;并使用统一的方式进行自动化构建&#xff0c;是Java 项目不可缺少的工具。 Maven 的作用 提供标准化的项目结构&#xff1a;以前不同的开发工具创建的项目结构是不一样的&#xf…

2025Github介绍与注册(有图片讲解,保姆级)

为什么要注册Github账号 利于团队协作&#xff0c;特别是打比赛的队友 版本控制强大&#xff0c;代码安全 开源项目多&#xff0c;方便个人模仿或抄袭 方便托管&#xff0c;形成自动化工具链 教育福利&#xff0c;教育参与者暂时免费 讲解完了优势&#xff0c;下面讲注册 Gith…

RUI电视桌面中文版:下载安装教程及桌面固件包获取全攻略

在智能电视的使用过程中&#xff0c;一款出色的桌面系统能极大提升用户体验&#xff0c;RUI电视桌面中文版就是这样一个不错的选择。下面为大家详细介绍RUI电视桌面中文版的下载安装教程以及桌面固件包的获取方法。 一、桌面固件包获取 首先是获取桌面固件包。可以通过RUI官方…

OpenAI 34页最佳构建Agent实践

penAI发布O4&#xff0c;也发布34页最佳构建Agent实践&#xff0c;值得阅读。 什么是Agent&#xff1f; 传统软件使用户能够简化和自动化工作流程&#xff0c;而代理能够以高度独立的方式代表用户执行相同的工作流程。 代理是能够独立地代表您完成任务的系统。 工作流程是必…

HOOPS Exchange 与HOOPS Communicator集成:打造工业3D可视化新标杆!

一、概述 在工业3D开发、BIM建筑、数字孪生和仿真分析等高端应用场景中&#xff0c;数据格式复杂、模型体量庞大、实时交互体验要求高&#xff0c;一直是困扰开发者的难题。Tech Soft 3D旗下的HOOPS Exchange和HOOPS Communicator&#xff0c;正是解决这类问题的黄金搭档。二者…

C#进阶学习(六)单向链表和双向链表,循环链表(下)循环链表

目录 &#x1f4ca; 链表三剑客&#xff1a;特性全景对比表 一、循环链表节点类 二、循环链表的整体设计框架 三、循环列表中的重要方法&#xff1a; &#xff08;1&#xff09;头插法&#xff0c;在头结点前面插入新的节点 &#xff08;2&#xff09;尾插法实现插入元素…

Unity使用Newtonsoft.Json本地化存档

我是标题 1.依赖包2.原理&#xff1a;3.代码4.可用优化5.数据加密 1.依赖包 Newtonsoft请在PacakgeManager处下载。 参考&#xff1a;打工人小棋 2.原理&#xff1a; 把要存储的对象数据等使用JsonConvert.SerializeObject(object T)进行序列化为字符串&#xff0c;并且通过…

2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(六级)真题

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;六级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 答案解析&#xff1a;https://blog.csdn.net/qq_33897084/article/details/147341458 一、单选题(共25题&#xff0c;共50分) 1. 在tkinter的…

Python 浮点数运算之谜:深入解析round(0.675, 2)等输出异常

一、问题背景&#xff1a;当浮点数运算遇见 “反直觉” 结果 在 Python 开发中&#xff0c;以下代码输出常让开发者困惑&#xff1a; print(round(0.675, 2)) # 预期0.67&#xff0c;实际0.68||预期0.68&#xff0c;实际0.67 print(0.1 0.2) # 预期0.3&…

【C#】Html转Pdf,Spire和iTextSharp结合,.net framework 4.8

&#x1f339;欢迎来到《小5讲堂》&#x1f339; &#x1f339;这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。&#x1f339; &#x1f339;温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01;&#…

极狐GitLab 注册限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 注册限制 (BASIC SELF) 您可以对注册实施以下限制&#xff1a; 禁用新注册。新注册需要管理员批准。需要用户电子邮件确认。…

利用大模型实现地理领域文档中英文自动化翻译

一、 背景描述 在跨国性企业日常经营过程中&#xff0c;经常会遇到专业性较强的文档翻译的需求&#xff0c;例如法律文书、商务合同、技术文档等&#xff1b;以往遇到此类场景&#xff0c;企业内部往往需要指派专人投入数小时甚至数天来整理和翻译&#xff0c;效率低下&#x…

SGFormer:卫星-地面融合 3D 语义场景补全

论文介绍 题目&#xff1a;SGFormer: Satellite-Ground Fusion for 3D Semantic Scene Completion 会议&#xff1a;IEEE / CVF Computer Vision and Pattern Recognition Conference 论文&#xff1a;https://www.arxiv.org/abs/2503.16825 代码&#xff1a;https://githu…

Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化

一、软件介绍 文末提供源码和程序下载学习 Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化。Trinity 提供性能分析和 XAI 工具&#xff0c;非常适合深度学习系统或其他执行复杂分类或解码的模型。 二、软件作用和特征 Trinity 通过结合具有超维感知能力的不同交…

城市街拍暗色电影胶片风格Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 城市街拍暗色电影胶片风格 Lr 调色&#xff0c;是借助 Adobe Lightroom 软件&#xff0c;为城市街拍的人像或场景照片赋予独特视觉风格的后期处理方式。旨在模拟电影胶片质感&#xff0c;营造出充满故事感与艺术感的暗色氛围&#xff0c;让照片仿佛截取于某部充满张力…

加密和解密(大语言模型)

看到很多对matlab的p文件加密方案感兴趣的。网络上技术资料比较少&#xff0c;所以&#xff0c;我让大语言模型提供一些概论性质的东西&#xff0c;转发出来自娱自乐。期望了解p文件加密的复杂度&#xff0c;而不是一定要尝试挑战加密算法。 但根据大语言模型提供的材料&#…

双轮驱动能源革命:能源互联网与分布式能源赋能工厂能效跃迁

在全球能源结构深度转型与“双碳”目标的双重驱动下&#xff0c;工厂作为能源消耗的主力军&#xff0c;正站在节能变革的关键节点。能源互联网与分布式能源技术的融合发展&#xff0c;为工厂节能开辟了全新路径。塔能科技凭借前沿技术与创新实践&#xff0c;深度探索能源协同优…

React 更新 state 中的数组

更新 state 中的数组 数组是另外一种可以存储在 state 中的 JavaScript 对象&#xff0c;它虽然是可变的&#xff0c;但是却应该被视为不可变。同对象一样&#xff0c;当你想要更新存储于 state 中的数组时&#xff0c;你需要创建一个新的数组&#xff08;或者创建一份已有数组…

ubantu18.04HDFS编程实践(Hadoop3.1.3)

说明&#xff1a;本文图片较多&#xff0c;耐心等待加载。&#xff08;建议用电脑&#xff09; 注意所有打开的文件都要记得保存。 第一步&#xff1a;准备工作 本文是在之前Hadoop搭建完集群环境后继续进行的&#xff0c;因此需要读者完成我之前教程的所有操作。 第二步&am…

MySQL快速入门篇---库的操作

目录 一、创建数据库 1.语法 2.示例 二、查看数据库 1.语法 三、字符集编码和校验&#xff08;排序&#xff09;规则 1.查看数据库支持的字符集编码 2.查看数据库支持的排序规则 3.查看系统默认字符集和排序规则 3.1.查看系统默认字符集 3.2.查看系统默认排序规则 ​…