經過郵件找回密碼功能的實現java
一、最近開發一個系統,有個需求就是,忘記密碼後經過郵箱找回。如今的系統在註冊的時候都會強制輸入郵箱,其一目的就是 經過郵件綁定找回,能夠進行密碼找回。經過java發送郵件的功能我就不說了,重點講找回密碼。mysql
二、參考別人的思路:發送郵件→請求郵件裏的URL→驗證url→{驗證成功修改密碼,不成功跳轉到失敗頁面}linux
重點就是如何生成這個url和如何解析這個url.
須要注意的是一個url只能修改一次密碼,當同一賬號發送多封郵件,只有最後一封郵件的url 郵箱git
三、加密能防止僞造攻擊,一次url只能驗證一次,而且綁定了用戶。生成url: 能夠用UUID生成隨機密鑰。 web
數字簽名 = MD5(用戶名+'$'+過時時間+‘$’+密鑰key)
數據庫字段(用戶名(主鍵),密鑰key,過時時間)
url參數(用戶名,數字簽名) ,密鑰key的生成:在每個用戶找回密碼時候爲這個用戶生成一個密鑰key ,spring
url example:http://www.wechat68.com:80/CardSSHOK/checkLink?sid=K3xHOi4o/UihH5QYWBDfYA==&userName=123sql
生成過時時間,生成數字簽名,生成url,發送郵件. AddU(用戶名,密鑰key,過時時間) 數據庫
使用到的數據庫以下
session
package com.soq.card.web.action; import java.sql.Timestamp; import java.util.List; import java.util.UUID; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.springframework.orm.hibernate3.HibernateTemplate; import com.soq.card.biz.UserHander; import com.soq.card.entity.Users; import com.soq.card.tools.DBhepler; import com.soq.card.tools.Mail; import com.soq.card.tools.Md5; import com.soq.card.web.base.BaseAction; /** * @author javen * @Email zyw205@gmail.com * */ public class PassEmailAction extends BaseAction { private Users users; private UserHander userHander; private String email; private String sid; private String userName; public String sendmail() { try { HibernateTemplate ht = this.getUserHander().getUsersDAO().getHibernateTemplate(); SessionFactory factory = ht.getSessionFactory(); Session session = factory.openSession(); Criteria criteria = session.createCriteria(Users.class); criteria.add(Restrictions.eq("loginName", email)); List<Users> list = criteria.list(); if (list.size() > 0) { users=list.get(0); Mail mail = new Mail(); String secretKey = UUID.randomUUID().toString(); // 密鑰 Timestamp outDate = new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000);// 30分鐘後過時 long date = outDate.getTime() / 1000 * 1000;// 忽略毫秒數 mySql 取出時間是忽略毫秒數的 DBhepler bhepler=new DBhepler(); String sql="update users set outDate=?,validataCode=? where loginName=?;"; String str[] ={outDate+"",secretKey,users.getLoginName()}; bhepler.AddU(sql, str); //this.getUserHander().getUsersDAO().getHibernateTemplate().update(users); // 保存到數據庫 System.out.println(" UserName>>>> "+users.getUserName()); String key =users.getUserName() + "$" + date + "$" + secretKey; System.out.println(" key>>>"+key); String digitalSignature = Md5.md5(key);// 數字簽名 String path = this.getRequest().getContextPath(); String basePath = this.getRequest().getScheme() + "://" + this.getRequest().getServerName() + ":" + this.getRequest().getServerPort() + path + "/"; String resetPassHref = basePath + "checkLink?sid=" + digitalSignature +"&userName="+users.getUserName(); String emailContent = "請勿回覆本郵件.點擊下面的連接,重設密碼<br/><a href=" + resetPassHref + " target='_BLANK'>" + resetPassHref + "</a> 或者 <a href=" + resetPassHref + " target='_BLANK'>點擊我從新設置密碼</a>" + "<br/>tips:本郵件超過30分鐘,連接將會失效,須要從新申請'找回密碼'" + key + "\t" + digitalSignature; mail.setTo(email); mail.setFrom("XX");// 你的郵箱 mail.setHost("smtp.163.com"); mail.setUsername("XXX@163.com");// 用戶 mail.setPassword("CXXX");// 密碼 mail.setSubject("[二維碼名片]找回您的帳戶密碼"); mail.setContent(emailContent); if (mail.sendMail()) { System.out.println(" 發送成功"); this.getRequest().setAttribute("mesg", "重置密碼郵件已經發送,請登錄郵箱進行重置!"); return "sendMail"; } } else { this.getRequest().setAttribute("mesg", "用戶名不存在,你不會忘記郵箱了吧?"); return "noUser"; } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return null; } public String checkResetLink() { System.out.println("sid>>>" + sid); if (sid.equals("") || userName.equals("")) { this.getRequest().setAttribute("mesg", "連接不完整,請從新生成"); System.out.println(">>>>> null"); return "error"; } HibernateTemplate ht = this.getUserHander().getUsersDAO().getHibernateTemplate(); SessionFactory factory = ht.getSessionFactory(); Session session = factory.openSession(); Criteria criteria = session.createCriteria(Users.class); criteria.add(Restrictions.eq("userName", userName)); List<Users> list = criteria.list(); if (list.size()>0) { users=list.get(0); Timestamp outDate = (Timestamp) users.getOutDate(); System.out.println("outDate>>>"+outDate); if(outDate.getTime() <= System.currentTimeMillis()){ //表示已通過期 this.getRequest().setAttribute("mesg", "連接已通過期,請從新申請找回密碼."); System.out.println("時間 超時"); return "error"; } String key = users.getUserName()+"$"+outDate.getTime()/1000*1000+"$"+users.getValidataCode();//數字簽名 System.out.println("key link》》"+key); String digitalSignature = Md5.md5(key);// 數字簽名 System.out.println("digitalSignature>>>>"+digitalSignature); if(!digitalSignature.equals(sid)) { this.getRequest().setAttribute("mesg", "連接不正確,是否已通過期了?從新申請吧."); System.out.println("標示不正確"); return "error"; }else { //連接驗證經過 轉到修改密碼頁面 this.getRequest().setAttribute("user", users); return "success"; } }else { this.getRequest().setAttribute("mesg", "連接錯誤,沒法找到匹配用戶,請從新申請找回密碼."); System.out.println("用戶不存在"); return "error"; } } public Users getUsers() { return users; } public void setUsers(Users users) { this.users = users; } public UserHander getUserHander() { return userHander; } public void setUserHander(UserHander userHander) { this.userHander = userHander; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
補充1:Timestamp類型對象在保存到數據的時候 毫秒精度會丟失。好比:2014-05-20 10:30:10.234 存到mysql數據庫的時候 變成 2013-05-20 10:30:10.0。時間變得不相同了,sid 匹配的時候不會相等。 因此我作了忽略精度的操做。dom
補充2:解決linux下面title中文亂碼
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder(); mailMessage.setSubject(MimeUtility.encodeText(mailInfo.getSubject(), "UTF-8", "B")); //解決linux郵件title亂碼
補充3:怎麼不直接把sid插入到users表呢。驗證的時候直接比較sid就ok了。
源碼下載地址 連接:http://pan.baidu.com/s/1sj1LBf3 密碼:fa4x