邮箱验证流程与token生成

本文介绍了邮箱验证的过程,包括使用SMTP发送邮件、Guava Cache实现验证码缓存以及JWT的生成。同时,讨论了跨域问题的原因、处理方式和JSONP请求的使用。此外,还涉及了密码格式检查和邮箱格式校验的实现。
摘要由CSDN通过智能技术生成

该过程仅供参考,若有优化与建议,欢迎提出。

依赖

		<!-- 发送邮件依赖 -->
      	<dependency>
      		<groupId>javax.mail</groupId>
      		<artifactId>mail</artifactId>
      		<version>1.4.7</version>
    	</dependency>
		<!-- 谷歌缓存依赖 -->
      	<dependency>
    		<groupId>com.google.guava</groupId>
    		<artifactId>guava</artifactId>
    		<version>28.2-jre</version>
    	</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 = "<!DOCTYPE html>\r\n" + 
				"<html lang=\"en\"" + 
				"<head>\r\n" + 
				"	<meta charset=\"UTF-8\"/>\r\n" + 
				"	<title>验证码</title>\r\n" + 
				"</head>\r\n" + 
				"	<body>\r\n" + 
				"	<h1>欢迎您使用XXX,这是您的验证码,请于X分钟内输入激活码</h1>\r\n" + 
				"	<p>"+ code +"</p>"+
				"	</body>\r\n" + 
				"</html>";
		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-8byteString 相互转换会不一样,因为无法表示,因此,使用的是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;
    }

JWT 工具类

该类使用于快捷生成token而准备的,该类封装了所需要的方法。
可以参看JWT —— 生成Token、解析Token的简单工具类

跨域问题

可以参考-跨域问题详解

使用XMLHttpReuqest (xhr)时,经常遇到的问题。属于JS的一种保护措施。

发生原因

  • 不同协议:即使是同一URL,当通过xhr访问却改变了协议时,也会被限制
  • 不同域名:当前域名和请求域名不同。或者主域名相同,子域名不同(Cookie限制)。使用域名对应的其中一个IP与域名时.
  • 不同端口:当前域名下不同端口

限制内容

  • Cookie、LocalStorage和IndexDB无法获取。
  • DOM元素无法获得。
  • AJAX请求不能发送。

处理方式

发送JSONP请求替代XHR请求

JSONP请求的类型是JavaScript脚本(callback 作为前后端的约定,callback的值做为方法名,json内容作为方法的参数),而XHR请求的类型是json类型。
注意,AbstractJsonpResponseBodyAdvice 类基本在Sprig Boot中被废弃。

//ajax前端
$.ajax({
    url: baseUrl + "/get1",
    dataType: "jsonp", // 关键字段
    jsonp: "callback", // 前后端默认的约定
    cache: true, // 表示请求结果可以被缓存,url中不会有下划线参数了
    success: function(json) {
        result = json;
    }
});

//Springboot服务端
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
	public JsonpAdvice() {
		super("callback");
	}
}

@CrossOrigin

它使用了一个额外的HTTP响应头来赋予当前user-agent(浏览器)获得在当前源(origin) 下使用非同源资源的权限 。(也就是加了一些信息,让它“看上去”同源)

使用方式:
直接在RequestMapping或者Controller注释的类/方法上添加该注释,Springboot就会完成相应的处理。

其有两个参数,若配置更细致,可以使用:

  • Origins : 允许可访问的域列表
  • maxAge:准备响应前的缓存持续的最大时间(以秒为单位)

其他方法

也有其他方法,比如服务器修改,反向代理等参看-跨域问题详解

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SSM框架(Spring+SpringMVC+MyBatis)是一种常用的Java Web开发框架,它集成了Spring、SpringMVC和MyBatis三个开源框架,可以帮助我们快速搭建和开发Java Web应用。 要实现邮箱验证功能,可以按照以下步骤进行: 1. 配置邮件服务器:首先需要配置一个可用的邮件服务器,比如使用JavaMail API连接SMTP服务器发送邮件。可以使用Spring提供的JavaMailSender来简化邮件发送的操作。 2. 编写发送邮件的代码:在需要发送验证邮件的地方,调用JavaMailSender发送邮件。可以设置邮件的收件人、主题、内容等信息,并将验证链接作为邮件内容发送给用户。 ```java @Autowired private JavaMailSender javaMailSender; public void sendVerificationEmail(String to, String verificationLink) { SimpleMailMessage message = new SimpleMailMessage(); message.setTo(to); message.setSubject("邮箱验证"); message.setText("请点击以下链接完成邮箱验证:" + verificationLink); javaMailSender.send(message); } ``` 3. 生成验证链接:在用户注册或者需要验证邮箱的地方,生成一个唯一的验证链接,并将该链接保存到数据库中。可以使用UUID来生成唯一标识符,并将该标识符作为参数拼接到验证链接中。 4. 验证链接的处理:当用户点击验证链接时,后台需要对该链接进行处理。可以在SpringMVC的Controller中定义一个处理验证链接的方法,通过获取链接中的参数,比对数据库中保存的验证链接,判断验证链接的有效性。 ```java @RequestMapping("/verifyEmail") public String verifyEmail(@RequestParam("token") String token) { // 根据token查询数据库,判断验证链接的有效性 // 如果验证链接有效,则更新用户的邮箱验证状态 // 返回相应的页面或者提示信息 } ``` 以上是实现SSM框架下邮箱验证功能的一般步骤。具体实现还需要根据项目的具体需求和架构进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值