SpringMVC通過郵件找回密碼功能的實現


1、最近開發一個系統,有個需求就是,忘記密碼后通過郵箱找回。現在的系統在注冊的時候都會強制輸入郵箱,其一目的就是 通過郵件綁定找回,可以進行密碼找回。通過java發送郵件的功能我就不說了,重點講找回密碼。

 

2、參考別人的思路:發送郵件→請求郵件里的URL→驗證url→{驗證成功修改密碼,不成功跳轉到失敗頁面}

重點就是如何生成這個url和如何解析這個url. 
需要注意的是一個url只能修改一次密碼,當同一帳號發送多封郵件,只有最后一封郵件的url

 

3、加密能防止偽造攻擊,一次url只能驗證一次,並且綁定了用戶。生成url: 可以用UUID生成隨機密鑰。

數字簽名 = MD5(用戶名+'$'+過期時間+‘$’+密鑰key) 
數據庫字段(用戶名(主鍵),密鑰key,過期時間) 
url參數(用戶名,數字簽名) ,密鑰key的生成:在每一個用戶找回密碼時候為這個用戶生成一個密鑰key ,

url example: http://localhost:8080/user/reset_password?sid=D622D6A23FBF86FFE696B593D55351A54AEAEA77&userName=test4

生成過期時間,生成數字簽名,生成url,發送郵件. saveOrUpdate(用戶名,密鑰key,過期時間) 

以下為springMvc代碼

 1  @RequestMapping(value = "/user/i_forget_password")
 2     @ResponseBody
 3     public Map forgetPass(HttpServletRequest request,String userName){
 4         Users users = userService.findUserByName(userName);
 5         Map map = new HashMap<String ,String >();
 6         String msg = "";
 7         if(users == null){              //用戶名不存在
 8             msg = "用戶名不存在,你不會忘記用戶名了吧?";
 9             map.put("msg",msg);
10             return map;
11         }
12         try{
13             String secretKey= UUID.randomUUID().toString();  //密鑰
14             Timestamp outDate = new Timestamp(System.currentTimeMillis()+30*60*1000);//30分鍾后過期
15             long date = outDate.getTime()/1000*1000;                  //忽略毫秒數
16             users.setValidataCode(secretKey);
17             users.setRegisterDate(outDate);
18             userService.update(users);    //保存到數據庫
19             String key = users.getUserName()+"$"+date+"$"+secretKey;
20             String digitalSignature = MD5.MD5Encode(key);                 //數字簽名
21 
22             String emailTitle = "有方雲密碼找回";
23             String path = request.getContextPath();
24             String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
25             String resetPassHref =  basePath+"user/reset_password?sid="+digitalSignature+"&userName="+users.getUserName();
26             String emailContent = "請勿回復本郵件.點擊下面的鏈接,重設密碼<br/><a href="+resetPassHref +" target='_BLANK'>點擊我重新設置密碼</a>" +
27                     "<br/>tips:本郵件超過30分鍾,鏈接將會失效,需要重新申請'找回密碼'"+key+"\t"+digitalSignature;
28             System.out.print(resetPassHref);
29             SendMail.getInstatnce().sendHtmlMail(emailTitle,emailContent,users.getEmail());
30             msg = "操作成功,已經發送找回密碼鏈接到您郵箱。請在30分鍾內重置密碼";
31             logInfo(request,userName,"申請找回密碼");
32         }catch (Exception e){
33             e.printStackTrace();
34             msg="郵箱不存在?未知錯誤,聯系管理員吧。";
35         }
36         map.put("msg",msg);
37         return map;
38     }

找回鏈接已經發到郵箱了。進入郵箱點開鏈接

以下為鏈接檢驗代碼,驗證通過 跳轉到修改密碼界面,否則跳轉到失敗界面

 1 @RequestMapping(value = "/user/reset_password",method = RequestMethod.GET)
 2     public ModelAndView checkResetLink(String sid,String userName){
 3         ModelAndView model = new ModelAndView("error");
 4         String msg = "";
 5         if(sid.equals("") || userName.equals("")){
 6             msg="鏈接不完整,請重新生成";
 7             model.addObject("msg",msg) ;
 8             logInfo(userName,"找回密碼鏈接失效");
 9             return model;
10         }
11         Users users = userService.findUserByName(userName);
12         if(users == null){
13             msg = "鏈接錯誤,無法找到匹配用戶,請重新申請找回密碼.";
14             model.addObject("msg",msg) ;
15             logInfo(userName,"找回密碼鏈接失效");
16             return model;
17         }
18         Timestamp outDate = users.getRegisterDate();
19         if(outDate.getTime() <= System.currentTimeMillis()){         //表示已經過期
20             msg = "鏈接已經過期,請重新申請找回密碼.";
21             model.addObject("msg",msg) ;
22             logInfo(userName,"找回密碼鏈接失效");
23             return model;
24         }
25         String key = users.getUserName()+"$"+outDate.getTime()/1000*1000+"$"+users.getValidataCode();          //數字簽名
26         String digitalSignature = MD5.MD5Encode(key);
27         System.out.println(key+"\t"+digitalSignature);
28         if(!digitalSignature.equals(sid)) {
29             msg = "鏈接不正確,是否已經過期了?重新申請吧";
30             model.addObject("msg",msg) ;
31             logInfo(userName,"找回密碼鏈接失效");
32             return model;
33         }
34         model.setViewName("user/reset_password");  //返回到修改密碼的界面
35         model.addObject("userName",userName);
36         return model;
37     }

補充1:Timestamp類型對象在保存到數據的時候 毫秒精度會丟失。比如:2013-10-08 10:29:10.234 存到mysql數據庫的時候 變成 2013-10-08 10:29:10.0。時間變得不相同了,sid 匹配的時候不會相等。 所以我做了忽略精度的操作。

補充2:解決linux下面title中文亂碼

1   sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
2   mailMessage.setSubject(MimeUtility.encodeText(mailInfo.getSubject(), "UTF-8", "B"));      //解決linux郵件title亂碼

補充3:怎么不直接把sid插入到user表呢。驗證的時候直接比較sid就ok了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM