目录
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)
//在发送验证码后,用于显示等待用户再次发送验证码的剩余时间,60spublic 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();}