找回密码是系统登录中比较常见的操作。当用户忘记密码的时候,输入注册的邮箱后,系统会自动发送一个链接地址至注册邮箱,用户去邮箱点击链接地址后重新设置密码。新密码填写完成后提交,密码修改完成。此时用户就可以用新的密码重新登录系统了。

  1. 点击页面的找回密码链接以后在界面填写注册邮箱地址,然后点击提交;
  2. 后台对提交的邮箱地址进行验证,如果邮箱还未注册则提示邮箱地址无效,如果邮箱存在则发送链接到邮箱;
  3. 链接地址是一个URL,此URL的设计是一个重点,要具有唯一性和实效性,防止别人伪造URL地址来窃取修改密码;
  4. 用户点击邮箱里的URL地址,跳转到重置密码页面,用户重新填写密码后提交,则密码修改成功。

下面首先我们先来看URL生成的代码:

//向邮箱发送找回密码链接
public void sendPwdEmail(){

    //邮件类,自己对此类进行封装
    Mail mail = new Mail();
    String email = getPara("email");
    
    //验证该邮箱是否存在
    User user = User.dao.getUserByEmail(email);
    if(user!=null){
        //设置此链接的有效期为30分钟
        Timestamp outDate = new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000);// 30分钟后过期
        long date = outDate.getTime() / 1000 * 1000;// 忽略毫秒数  mySql 取出时间是忽略毫秒数的
        
        //随机生成15位的字母数字字符串
        String secretKey = CommonUtil.getRandomString(15);
        //数字加一串随机字符,生成唯一识别码
        String digitalSignature = date + "$" + secretKey;
        
        //更新用户表,更新唯一码和链接到期时间字段       
        User.dao.updateUsersOutdate(email, outDate, digitalSignature);
                
        String path = this.getRequest().getContextPath();
        String basePath = this.getRequest().getScheme() + "://"
                        + this.getRequest().getServerName() + ":"
                        + this.getRequest().getServerPort() + path + "/";
                        
        //找回密码链接URL地址
        String resetPassHref = basePath + "login/resetPwd?sid="
                        + digitalSignature +"&userName="+ user.getStr("username");
        String emailContent = "请勿回复本邮件.点击下面的链接,重设密码<br/><a href="
                        + resetPassHref + " target='_BLANK'>" + resetPassHref
                        + "</a> " + "<br/>tips:本邮件超过30分钟链接将会失效,需要重新申请找回密码.";
        mail.setTo(email);
        mail.setSubject("邮箱找回您的账号密码!");
        mail.setContent(emailContent);
        mail.send();
        json.put("error", 0);
        json.put("msg", "邮件发送成功!请到邮箱查收密码链接!");
    }else{
        json.put("error", 1);
        json.put("msg", "该邮箱尚未注册!");
    }

}
//向邮箱发送找回密码链接
public void sendPwdEmail(){

    //邮件类,自己对此类进行封装
    Mail mail = new Mail();
    String email = getPara("email");
    
    //验证该邮箱是否存在
    User user = User.dao.getUserByEmail(email);
    if(user!=null){
        //设置此链接的有效期为30分钟
        Timestamp outDate = new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000);// 30分钟后过期
        long date = outDate.getTime() / 1000 * 1000;// 忽略毫秒数  mySql 取出时间是忽略毫秒数的
        
        //随机生成15位的字母数字字符串
        String secretKey = CommonUtil.getRandomString(15);
        //数字加一串随机字符,生成唯一识别码
        String digitalSignature = date + "$" + secretKey;
        
        //更新用户表,更新唯一码和链接到期时间字段       
        User.dao.updateUsersOutdate(email, outDate, digitalSignature);
                
        String path = this.getRequest().getContextPath();
        String basePath = this.getRequest().getScheme() + "://"
                        + this.getRequest().getServerName() + ":"
                        + this.getRequest().getServerPort() + path + "/";
                        
        //找回密码链接URL地址
        String resetPassHref = basePath + "login/resetPwd?sid="
                        + digitalSignature +"&userName="+ user.getStr("username");
        String emailContent = "请勿回复本邮件.点击下面的链接,重设密码<br/><a href="
                        + resetPassHref + " target='_BLANK'>" + resetPassHref
                        + "</a> " + "<br/>tips:本邮件超过30分钟链接将会失效,需要重新申请找回密码.";
        mail.setTo(email);
        mail.setSubject("邮箱找回您的账号密码!");
        mail.setContent(emailContent);
        mail.send();
        json.put("error", 0);
        json.put("msg", "邮件发送成功!请到邮箱查收密码链接!");
    }else{
        json.put("error", 1);
        json.put("msg", "该邮箱尚未注册!");
    }

}


当用户去邮箱点击链接地址时,此时需要解析URL地址是否有效,方法如下

//验证链接地址的有效性,重设密码
public void resetPwd(){
        //从URL获取sid和username参数
	String sid = getPara("sid");
	String uname =getPara("userName");
	if(sid==null || "".equals(sid) || uname==null || "".equals(uname)){
		render("redirect.html");
	}else{
		User user = User.dao.getUserByName(uname);
		String code = user.getStr("validataCode");
		Timestamp ts = user.getTimestamp("outdate");
		//URL地址只在半小时内有效,超过时间则无效,需重新申请
		if(user !=null && code!=null && code.equals(sid) &&ts.getTime()>= System.currentTimeMillis()){
			setAttr("email",user.get("email"));
			render("resetpwd.html");
		} else {
			render("redirect.html");
		}
			
	}		
}
//验证链接地址的有效性,重设密码
public void resetPwd(){
        //从URL获取sid和username参数
	String sid = getPara("sid");
	String uname =getPara("userName");
	if(sid==null || "".equals(sid) || uname==null || "".equals(uname)){
		render("redirect.html");
	}else{
		User user = User.dao.getUserByName(uname);
		String code = user.getStr("validataCode");
		Timestamp ts = user.getTimestamp("outdate");
		//URL地址只在半小时内有效,超过时间则无效,需重新申请
		if(user !=null && code!=null && code.equals(sid) &&ts.getTime()>= System.currentTimeMillis()){
			setAttr("email",user.get("email"));
			render("resetpwd.html");
		} else {
			render("redirect.html");
		}
			
	}		
}


发送的链接地址形式大致为:

http://localhost:80/login/resetPwd?sid=1438334705000$CF3ZG5eUQkRUqAh&userName=admins

链接若有效,则在页面重新填写新的密码然后提交,则密码更新成功。

附邮件类Mail类的封装:

public class Mail {
	//邮件标题
	private String subject;
	//邮件正文
	private String content;
	//邮件发送地址
	private InternetAddress from;
	//邮件接收地址
	private InternetAddress to;
	
	public Mail() {
	    try {
	        this.from = new InternetAddress(DictKeys.EMAIL_USERNAME);
	        this.to = new InternetAddress(DictKeys.EMAIL_USERNAME);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	    this.subject = "From Jfinal";
	}
	
	public void setTo(String to) {
	    try {
		this.to = new InternetAddress(to);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
	
	public void setFrom(String from) {
	    try {
		this.from = new InternetAddress(from);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
	
	public String getSubject() {
	    return subject;
	}
	public void setSubject(String subject) {
	    this.subject = subject;
	}
	public String getContent() {
	    return content;
	}
	public void setContent(String content) {
	    this.content = content;
	}
	public InternetAddress getFrom() {
	    return from;
	}

	public InternetAddress getTo() {
	    return to;
	}
        
        //发送邮件方法
	public void send(){
		new Thread(new Runnable() {
			@Override
			public void run() {
			        //设置属性
				Properties props = new Properties();
				props.setProperty("mail.transport.protocol", DictKeys.EMAIL_PROTOCOL);
				props.setProperty("mail.smtp.host", DictKeys.EMAIL_HOST);
				props.setProperty("mail.smtp.auth", "true");
				
				//邮件授权,需要邮件用户名、密码
				Authenticator auth = new EmailAuth(DictKeys.EMAIL_USERNAME,DictKeys.EMAIL_PASSWORD);
				Session session = Session.getDefaultInstance(props, auth);
				MimeMessage message = new MimeMessage(session);
				try {
					message.setSubject(getSubject());
					message.setFrom(getFrom());
					message.setRecipient(RecipientType.TO, getTo());
					message.setSentDate(new Date());
					message.setContent(content, "text/html;charset=UTF-8");
					Transport.send(message);
				} catch (MessagingException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

}
public class Mail {
	//邮件标题
	private String subject;
	//邮件正文
	private String content;
	//邮件发送地址
	private InternetAddress from;
	//邮件接收地址
	private InternetAddress to;
	
	public Mail() {
	    try {
	        this.from = new InternetAddress(DictKeys.EMAIL_USERNAME);
	        this.to = new InternetAddress(DictKeys.EMAIL_USERNAME);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	    this.subject = "From Jfinal";
	}
	
	public void setTo(String to) {
	    try {
		this.to = new InternetAddress(to);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
	
	public void setFrom(String from) {
	    try {
		this.from = new InternetAddress(from);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
	
	public String getSubject() {
	    return subject;
	}
	public void setSubject(String subject) {
	    this.subject = subject;
	}
	public String getContent() {
	    return content;
	}
	public void setContent(String content) {
	    this.content = content;
	}
	public InternetAddress getFrom() {
	    return from;
	}

	public InternetAddress getTo() {
	    return to;
	}
        
        //发送邮件方法
	public void send(){
		new Thread(new Runnable() {
			@Override
			public void run() {
			        //设置属性
				Properties props = new Properties();
				props.setProperty("mail.transport.protocol", DictKeys.EMAIL_PROTOCOL);
				props.setProperty("mail.smtp.host", DictKeys.EMAIL_HOST);
				props.setProperty("mail.smtp.auth", "true");
				
				//邮件授权,需要邮件用户名、密码
				Authenticator auth = new EmailAuth(DictKeys.EMAIL_USERNAME,DictKeys.EMAIL_PASSWORD);
				Session session = Session.getDefaultInstance(props, auth);
				MimeMessage message = new MimeMessage(session);
				try {
					message.setSubject(getSubject());
					message.setFrom(getFrom());
					message.setRecipient(RecipientType.TO, getTo());
					message.setSentDate(new Date());
					message.setContent(content, "text/html;charset=UTF-8");
					Transport.send(message);
				} catch (MessagingException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

}