该过程仅供参考,若有优化与建议,欢迎提出。
目录- 依赖
- 邮箱验证
- 邮箱工具类
- 检查邮箱格式
- Cache缓存
- SendEmail方法
- token 生成
<dependency>
<groupId>javax.mailgroupId>
<artifactId>mailartifactId>
<version>1.4.7version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>28.2-jreversion>
dependency>
邮箱验证
邮箱工具类
此处用的是线程的方式,Callable更容易设置是否超时
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.Callable;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SEmail implements Callable<Boolean>{
private static String USERNAME = "";//邮箱地址XXX@XX.XX
/*
* POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
*需要给你的邮箱开启该功能,并填写相应信息
*/
private static String PASSWORD = "";//填写你的邮箱的授权码
private static String HOST = "";//选择一个可用的服务器
private String email;//目标邮箱
private String code;//生成的验证码
public SEmail(String email,String code){
this.email = email;
this.code = code;
}
public Boolean call() throws Exception{
Transport transport = null;
Properties prop = new Properties();
prop.setProperty("mail.smtp.host", HOST);
prop.setProperty("mai.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth","true");
Session session = Session.getDefaultInstance(prop);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(USERNAME));
message.setRecipient(Message.RecipientType.TO,new InternetAddress(email));
message.setSentDate(new Date());
message.setSubject("邮件验证码");
String info = "\r\n" +
" +
"\r\n" +
" \r\n" +
" 验证码 \r\n" +
"\r\n" +
" \r\n" +
" 欢迎您使用XXX,这是您的验证码,请于X分钟内输入激活码\r\n" +
" "
+ code +""+
" \r\n" +
"";
message.setContent(info,"text/html;charset=UTF-8");
message.saveChanges();
session.setDebug(false);
transport = session.getTransport("smtp");
transport.connect(USERNAME,PASSWORD);
transport.sendMessage(message, message.getAllRecipients());
return true;
}
}
检查邮箱格式
public class CheckData {
private static String NUM = ".*\d+.*";
private static String SMALL = ".*[a-zA-Z]+.*";
private static String CAPTIAL= ".*[a-zA-Z]+.*";
private static String SYMBOL = ".*([~!@#$%^&*()_+|<>,.?/:;'\\[\\]{\\\\}\\\"]){1,}+.*";
private static String EMAIL = "(\w+([-.][A-Za-z0-9]+)*){3,18}@\w+([-.][A-Za-z0-9]+)*\.\w+([-.][A-Za-z0-9]+)*";
/*
*邮箱格式检查
*/
public static boolean checkEmail(String email) {
if (email != null) {
return email.matches(EMAIL);
}
return false;
}
/*
*密码格式检查,此处是必须要有带小写+数字+不能有特殊符号
*/
public static boolean checkPass(String password) {
if (password == null){
return false;
}
int size = password.length();
if(size < 6||size >16) {
return false;
}
if(password.matches(SYMBOL)) {
return false;
}
int n = (password.matches(SMALL) ? 1 : 0) + (password.matches(NUM) ? 1 : 0) + (password.matches(CAPTIAL) ? 1 : 0);
return n==3;
}
}
Cache缓存
使用谷歌的一款包
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public class VerifyCodeCache {
/**
* 5分钟的验证码缓存,保障线程安全
*/
private static volatile Cache<String,String> codeCache;
private VerifyCodeCache(){}
public static Cache<String,String> getCodeCache() {
//单例,懒汉模式
//双验证+同步锁
if (codeCache == null) {
synchronized (VerifyCodeCache.class) {
if (codeCache == null) {
codeCache = CacheBuilder.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.maximumSize(100)
.concurrencyLevel(50).build();
}
}
}
return codeCache;
}
}
SendEmail方法
/*
* 此处使用固定大小的线程池,限制可同时发送的数目
* 允许10个线程同时执行
* */
private ExecutorService executor = Executors.newFixedThreadPool(10);
/*
* 验证码缓存,单例
*/
private Cache<String,String> codeCache = VerifyCodeCache.getCodeCache();
@ResponseBody
@RequestMapping("")
public String sendEmail(String email){
...
if(!CheckData.checkEmail(email)) {
resMessage.setAll(StatusCode.send_fail,"邮箱错误");
...
}
StringBuilder builder = new StringBuilder();
Random random = new Random();
for(int i = 0 ; i<8;i++) {
builder.append(random.nextInt(9));
}
Future<Boolean> future = executor.submit(new SEmail(email,builder.toString()));
try {
//等待30秒,未执行完成,抛出中断异常
future.get(30, TimeUnit.SECONDS);
//放入对应邮箱以及验证码
codeCache.put(email, builder.toString());
...
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.getStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.getStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.getStackTrace();
}
...
}
token 生成
此处用的是Java自带的加密模式,以及自己的加密密钥
注意,UTF-8的byte与String 相互转换会不一样,因为无法表示,因此,使用的是ISO-8859-1
/*
*密钥
*/
private final static Key SECRET_KEY = new SecretKeySpec("密钥内容".getBytes(),"AES");
/*
* 生成key
* */
public static String createToken(String uuid) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
//获取当前时间
long currentTimeMillis = System.currentTimeMillis();
/*token中即为加密信息,可以自己随意设置
*此处为了方便,用的是uuid+有效时间
*TIME_OUT:为有时长,后通过比较,即可判断是否过期
*/
String token = uuid + ":"+(currentTimeMillis + TIME_OUT);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY);
return new String(cipher.doFinal(token.getBytes()),"ISO-8859-1");
}
/*
* 还原
* */
public static String transferToken(String token) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY);
byte[] newData = cipher.doFinal(token.getBytes("ISO-8859-1"));
String res = new String(newData);
return res;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)