一、发送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;
@Test
public void send1() {
String email = "xxxxxx@qq.com";//接收人邮箱
//HtmlEmail方式
sendEmail(email);
}
@Test
public 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 block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.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() {
@Override
protected 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) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long 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,并返回ID
long 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);
}
}
}