一、发送qq邮箱验证码以及倒计时
要发送验证码需要用到邮箱的授权码:
qq邮箱获取方式,打开qq邮箱点设置找到如下界面:
然后获取授权码;
导入依赖
<dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.6.2</version></dependency>
这个依赖有些都不一样,尽量按最新的来
代码:
package com.kgc.ymw.util;import java.security.GeneralSecurityException;
import java.util.Properties;
import java.util.Random;import javax.mail.Authenticator;
import javax.mail.Message;
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;import com.sun.mail.util.MailSSLSocketFactory;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.junit.jupiter.api.Test;/*** @author: BruceYoung* @date: 2023/5/11*/
@SuppressWarnings({"all"})
public class EmailTest {private static String yzm;@Testpublic void send1() {String email = "xxxxxx@qq.com";//接收人邮箱//HtmlEmail方式sendEmail(email);}@Testpublic void send2() {
// yzm = random1();try {//javax.mail方式(发送方的邮箱,qq邮箱中申请的16位授权码,接收人邮箱,邮件标题,邮件内容)sendMail("xxxx@qq.com", "授权码", "xxxxxx@qq.com", "名称", "<html><h1>邀请您注册验证码:" + yzm + "</h1></html>");} catch (MessagingException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (GeneralSecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/*** 方式1:发送QQ邮件*/public static String sendEmail(String email) {HtmlEmail send = new HtmlEmail();//创建一个HtmlEmail实例对象// 获取随机验证码yzm = random1();String resultCode = yzm;try {send.setHostName("smtp.qq.com");send.setAuthentication("xxxxx@qq.com", "邮箱授权码"); //第一个参数是发送者的QQEamil邮箱 第二个参数是刚刚获取的授权码send.setFrom("xxxxxx@qq.com", "名称");//发送人的邮箱为自己的,用户名可以随便填 记得是自己的邮箱不是qq
// send.setSmtpPort(465); //端口号 可以不开send.setSSLOnConnect(true); //开启SSL加密send.setCharset("utf-8");send.addTo(email); //设置收件人 email为你要发送给谁的邮箱账户send.setSubject("标题"); //邮箱标题send.setMsg("您的验证码为:<font color='red' > " + resultCode + " </font>,五分钟后失效"); //Eamil发送的内容send.send(); //发送} catch (EmailException e) {e.printStackTrace();}return yzm;}/*** 方式2:发送QQ邮件** @param sender 发送方的邮箱* @param auth qq邮箱中申请的16位授权码* @param to 接收人邮箱* @param title 邮件标题* @param content 邮件内容*/public static String sendMail(String sender, String auth, String to, String title, String content) throws MessagingException, GeneralSecurityException, javax.mail.MessagingException {yzm = random1();//创建一个配置文件并保存Properties properties = new Properties();properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");properties.setProperty("mail.smtp.socketFactory.fallback", "false");properties.setProperty("mail.smtp.port", "465");properties.setProperty("mail.smtp.socketFactory.port", "465");properties.setProperty("mail.host", "smtp.qq.com");properties.setProperty("mail.transport.protocol", "smtp");properties.setProperty("mail.smtp.auth", "true");//QQ存在一个特性设置SSL加密MailSSLSocketFactory sf = new MailSSLSocketFactory();sf.setTrustAllHosts(true);properties.put("mail.smtp.ssl.enable", "true");properties.put("mail.smtp.ssl.socketFactory", sf);//创建一个session对象Session session = Session.getDefaultInstance(properties, new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(sender, auth);}});//开启debug模式session.setDebug(true);//获取连接对象Transport transport = session.getTransport();//连接服务器transport.connect("smtp.qq.com", sender, auth);//创建邮件对象MimeMessage mimeMessage = new MimeMessage(session);//邮件发送人mimeMessage.setFrom(new InternetAddress(sender));//邮件接收人mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(to));//邮件标题mimeMessage.setSubject(title);//邮件内容mimeMessage.setContent(content, "text/html;charset=UTF-8");//发送邮件transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());//关闭连接transport.close();return yzm;}//生成6位数 验证码public static String random1() {String code = "";Random rd = new Random();for (int i = 0; i < 6; i++) {int r = rd.nextInt(10); //每次随机出一个数字(0-9)code = code + r; //把每次随机出的数字拼在一起}System.out.println(code);return code;}}
倒计时:
private void startCountdown() {Thread countdownThread = new Thread(()->{while(countdownSeconds>0) {try {Thread.sleep(1000);countdownSeconds--;updataButtonText(countdownSeconds+"s");} catch (InterruptedException e) {throw new RuntimeException(e);}}Platform.runLater(() -> {SendCodeButton.setText("发送");});});countdownThread.start();}private void updataButtonText(String s) {Platform.runLater(()->SendCodeButton.setText(s));}
点击发送按钮后调用startCountdown即可
二、雪花算法
Twitter
的分布式自增ID
算法,经过测试snowflake
每秒能够产生26
万个自增可排序的ID
。
Twitter
的雪花算法生成ID
能够按照时间有序生成- 雪花算法生成
id
的结果是一个64bit
大小的整数,为一个long
型 - 分布式系统内不会产生
ID
碰撞并且效率较高
号段解析:
- 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
- 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年,2的41次方 -1毫秒值就是69年
- 工作机器id(10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。可以部署在2^10=1024个节点,包括5位datacenterId和5位workerId
- 序列号部分(12bit):自增值支持同一毫秒内同一个节点可以生成4096个ID。12位可以表示的最大正整数2^12-1=4095,即可以用0,1,2,3…4094这4095个数字,来表示同一机器同一时间(1毫秒)内产生4095个ID序号。
//雪花算法代码实现
public class IdWorker {// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)private final static long twepoch = 1288834974657L;// 机器标识位数private final static long workerIdBits = 5L;// 数据中心标识位数private final static long datacenterIdBits = 5L;// 机器ID最大值private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);// 数据中心ID最大值private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 毫秒内自增位private final static long sequenceBits = 12L;// 机器ID偏左移12位private final static long workerIdShift = sequenceBits;// 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits;// 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final static long sequenceMask = -1L ^ (-1L << sequenceBits);/* 上次生产id时间戳 */private static long lastTimestamp = -1L;// 0,并发控制private long sequence = 0L;private final long workerId;// 数据标识id部分private final long datacenterId;public IdWorker(){this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** @param workerId* 工作机器ID* @param datacenterId* 序列号*/public IdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {// 当前毫秒内,则+1sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}private long tilNextMillis(final long lastTimestamp) {long timestamp = this.timeGen();while (timestamp <= lastTimestamp) {timestamp = this.timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}/*** <p>* 获取 maxWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected static long getDatacenterId(long maxDatacenterId) {long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInterface network = NetworkInterface.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] mac = network.getHardwareAddress();id = ((0x000000FF & (long) mac[mac.length - 1])| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}} catch (Exception e) {System.out.println(" getDatacenterId: " + e.getMessage());}return id;}public static void main(String[] args) {IdWorker idWorker = new IdWorker(0, 0);for (int i = 0; i < 10; i++) {long id = idWorker.nextId();System.out.println(id);System.out.println("========================");}}}
这个的话是源代码一般不用这个,下面这个将id缩短到16
// 毫秒内自增位
// private final static long sequenceBits = 12L;private final static long sequenceBits = 8L;// 时间毫秒左移22位
// private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final static long timestampLeftShift = sequenceBits + workerIdBits;public synchronized long nextId() {.。。。。。。。。。。// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)
// | (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}
三、MD5加密
MD5:即 Message Digest Algorithm 5 缩写,中文含义为信息摘要算法第5版,是一种被广泛使用的密码散列函数,可产生一个16字节的散列值,用来提供信息的完整性保护。对于软件开发者来说,经常使用 MD5 校验信息的完整性(比如,防止文件篡改或损坏),甚至将它当作加密算法使用。
MD5 作为一种摘要算法,具有以下特征:
1、产生固定长度的散列值。无论输入了多长的信息,经过 MD5 处理后都只会产生一个16字节的散列值。
2、不可逆。经过 MD5 处理后得到的散列结果,无法计算出原始数据,正是因为 MD5 无法从密文还原成明文,它不能用于解密了。
3、运算快。MD5 采用的是位运算,速度很快,几乎不占用 CPU 资源。
4、不安全。1996年后该算法被证实存在弱点,可以被加以破解。2004年,证实 MD5 算法无法防止碰撞,因此不适用于安全性认证,比如 SSL 公开密钥认证、数字签名等用途。2011年,RFC 6151 禁止 MD5 用作密钥散列消息认证码。
java代码:
import java.security.MessageDigest;
import java.util.Objects;public class MD5Util {private static final char[] HEX_DIGITS= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};private static String characterEncoding;public static String encode(String str) {if (Objects.isNull(str)) {return null;}try {MessageDigest md = MessageDigest.getInstance("MD5");md.update(Objects.nonNull(characterEncoding) ? str.getBytes(characterEncoding) : str.getBytes());byte[] bytes = md.digest();StringBuilder builder = new StringBuilder(bytes.length * 2);for (byte b : bytes) {builder.append(HEX_DIGITS[b >> 4 & 15]);builder.append(HEX_DIGITS[b & 15]);}return builder.toString();} catch (Exception e) {throw new RuntimeException("process failed ", e);}}
}