目录
1.雪花算法
2.MD5加密
3.小眼睛显示密码
4.发送验证码
5.倒计时
1.雪花算法
SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本
雪花算法的原理就是生成一个的 64 位比特位的 long 类型的唯一 id。
最高 1 位固定值 0,因为生成的 id 是正整数,如果是 1 就是负数了。
接下来 41 位存储毫秒级时间戳,2^41/(1000*60*60*24*365)=69,大概可以使用 69 年。
再接下 10 位存储机器码,包括 5 位 datacenterId 和 5 位 workerId。最多可以部署 2^10=1024 台机器。
最后 12 位存储序列号。同一毫秒时间戳时,通过这个递增的序列号来区分。即对于同一台机器而言,同一毫秒时间戳下,可以生成 2^12=4096 个不重复 id。
public class SnowflakeIdGenerator {
private final long twepoch = 1626708222000L; // 设置起始时间戳,这里使用当前时间的毫秒值作为起始时间
private final long workerIdBits = 10L;
private final long sequenceBits = 12L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long workerIdShift = sequenceBits;
private final long timestampLeftShift = sequenceBits + workerIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", maxWorkerId));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
// 示例用法
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 假设当前机器的worker id是1
// 生成10个ID进行展示
for (int i = 0; i < 10; i++) {
long id = idGenerator.nextId();
System.out.println("Generated Id: " + id);
}
}
}
2.MD5加密
MD5加密全程是Message-Digest Algoorithm 5(信息-摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串。
例如我们要加密一篇文章,那么我们会随机从每段话或者每行中获取一个字,把这些字统计出来后,再通过一定的运算获得一个固定长度的MD5加密后信息。因此,其很难被逆向破解。
MD5加密的特点:
MD5加密的特点主要有以下几点:
1、针对不同长度待加密的数据、字符串等等,其都可以返回一个固定长度的MD5加密字符串。(通常32位的16进制字符串);
2、其加密过程几乎不可逆,除非维护一个庞大的Key-Value数据库来进行碰撞破解,否则几乎无法解开。
3、运算简便,且可实现方式多样,通过一定的处理方式也可以避免碰撞算法的破解。
4、对于一个固定的字符串。数字等等,MD5加密后的字符串是固定的,也就是说不管MD5加密多少次,都是同样的结果。
public class MD5Util {
// 加密方法:接收一个字符串明文,返回使用 MD5 加密后的哈希值
public static String encrypt(String plaintext) throws NoSuchAlgorithmException {
// 使用 MD5 算法创建 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 更新 MessageDigest 对象中的字节数据
md.update(plaintext.getBytes());
// 对更新后的数据计算哈希值,存储在 byte 数组中
byte[] digest = md.digest();
// 将 byte 数组转换为十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
// 返回十六进制字符串
return sb.toString();
}
// 解密方法:接收一个字符串明文和一个使用 MD5 加密后的哈希值,返回解密结果(true 表示匹配,false 表示不匹配)
public static boolean decrypt(String plaintext, String encrypted) throws NoSuchAlgorithmException {
// 调用加密方法计算出明文的哈希值
String decrypted = encrypt(plaintext);
// 比较计算得到的哈希值和输入的哈希值是否相同
return decrypted.equals(encrypted);
}
}
3.小眼睛显示密码
方法:javafx中添加一个密码文本框(passwordFild),一个普通文本框(passwordlook),一个按钮(yanjingButton),两张图片(eyesz)(eyesb),按钮和两张图片位置重叠,按钮透明,睁眼的图片透明,闭眼的图片显示,设置按钮点击方法(yanjingButtonOnAction)如下。点击切换图片的透明和显示,切换两个文本框的透明与显示。
// 在类的成员变量中添加一个标志,用于记录是否已经绑定过文本框
private boolean isPasswordFieldsBound = false;
//小眼睛
public void yanjingButtonOnAction(ActionEvent actionEvent) {
Button eyesButton = (Button) actionEvent.getSource();
// 初始化绑定操作,只需执行一次
if (!isPasswordFieldsBound) {
passwordlook.textProperty().bindBidirectional(passwordField.textProperty());
isPasswordFieldsBound = true;
}
eyesButton.setOnMouseClicked(event -> {
String look = passwordField.getText();
passwordField.setText(look);
boolean isPasswordFieldVisible = passwordField.isVisible();
passwordField.setVisible(!isPasswordFieldVisible);
passwordlook.setVisible(isPasswordFieldVisible);
eyesb.setVisible(!isPasswordFieldVisible);
eyesz.setVisible(isPasswordFieldVisible);
});
}
4.发送验证码
注意:邮箱用自己的
同时想要如下导包:
发送验证码:
import java.util.Date;
import java.util.Properties;
import java.util.Random;
import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class MailUtils {
private static MimeMessage message;
private static String m;
public MailUtils() {
}
public static String sendEmail(String to) throws MessagingException {
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
// QQ邮箱服务器
String smtpHost = "smtp.qq.com";
// 邮箱用户名,即QQ账号(自定义)
String username = "2161672768";
// 邮箱授权码(自定义)
String password = "xbgwgdzxkqobdjih";
// 自己的邮箱(自定义)
String from = "2161672768@qq.com";
// 要发送的邮箱地址(自定义)
String toAddress = to;
//Transport transport;
Properties props = new Properties();
props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.port", "465");
props.setProperty("mail.smtp.socketFactory.port", "465");
props.setProperty("mail.smtp.auth", "true");
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.username", username);
props.put("mail.smtp.password", password);
Session session = Session.getDefaultInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
InternetAddress[] addresses = new InternetAddress[]{new InternetAddress(toAddress)};
message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipients(MimeMessage.RecipientType.TO, addresses);
message.setSubject("验证码");
message.setSentDate(new Date());
m = generateVerificationCode(5);
message.setText(m);
System.out.println(m);
Transport transport = session.getTransport("smtp");
transport.connect(smtpHost, username, password);
transport.sendMessage(message, message.getAllRecipients());
// Transport.send(message);
System.out.println("信息已经发送");
transport.close();
return m;
}
public static boolean judge(String s) {
return m.equals(s);
}
//随机生成长度为5的验证码
private static String generateVerificationCode(int length) {
String charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuilder verificationCode = new StringBuilder();
Random random = new Random();
for(int i = 0; i < length; ++i) {
verificationCode.append(charSet.charAt(random.nextInt(charSet.length())));
}
return verificationCode.toString();
}
}
结果如下:
5.倒计时
在发送验证码后,用于显示等待用户再次发送验证码的剩余时间,这里设置的是60s(注意:1000L=1s)
//在发送验证码后,用于显示等待用户再次发送验证码的剩余时间,60s
public void count(){
this.yzmButton.setVisible(false);
this.wait.setVisible(true);
(new Thread(() -> {
int st = 60;
while(st > 0) {
final int finalSt = st;
Platform.runLater(new Runnable() {
public void run() {
EmailLoginController.this.wait.setText(Integer.toString(finalSt));
}
});
try {
Thread.sleep(1000L);
--st;
} catch (InterruptedException var4) {
InterruptedException e = var4;
throw new RuntimeException(e);
}
}
this.yzmButton.setVisible(true);
this.wait.setVisible(false);
})).start();
}
方法用于实现较长时间的倒计时,这里设置的是120秒,用于显示验证码的有效时间。
//方法用于实现较长时间的倒计时(120秒),用于显示验证码的有效时间。
public void count00(){
this.time.setVisible(true);
(new Thread(() -> {
int st = 120;
while(st > 0) {
if (this.time1 != this.time2) {
this.time1 = this.time2;
st = 120;
}
final int finalSt = st;
Platform.runLater(new Runnable() {
public void run() {
EmailLoginController.this.time.setText("验证码有效时间剩余:" + Integer.toString(finalSt));
}
});
try {
Thread.sleep(1000L);
--st;
} catch (InterruptedException var4) {
InterruptedException e = var4;
throw new RuntimeException(e);
}
}
this.time.setVisible(false);
})).start();
}