最近開發(fā)一個系統(tǒng),有個需求就是,忘記密碼后通過郵箱找回。現(xiàn)在的系統(tǒng)在注冊的時候都會強制輸入郵箱,其一目的就是通過郵件綁定找回,可以進行密碼找回。通過java發(fā)送郵件的功能我就不說了,重點講找回密碼。
參考別人的思路:發(fā)送郵件→請求郵件里的URL→驗證url→{驗證成功修改密碼,不成功跳轉到失敗頁面}
重點就是如何生成這個url和如何解析這個url.
需要注意的是一個url只能修改一次密碼,當同一帳號發(fā)送多封郵件,只有最后一封郵件的url
加密能防止偽造攻擊,一次url只能驗證一次,并且綁定了用戶。生成url: 可以用UUID生成隨機密鑰。
數(shù)字簽名 = MD5(用戶名+'$'+過期時間+‘$'+密鑰key)
數(shù)據(jù)庫字段(用戶名(主鍵),密鑰key,過期時間)
url參數(shù)(用戶名,數(shù)字簽名) ,密鑰key的生成:在每一個用戶找回密碼時候為這個用戶生成一個密鑰key ,
url example: http://localhost:8080/user/reset_password?sid=D622D6A23FBF86FFE696B593D55351A54AEAEA77&userName=test4
生成過期時間,生成數(shù)字簽名,生成url,發(fā)送郵件. saveOrUpdate(用戶名,密鑰key,過期時間)
以下為springMvc代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
@RequestMapping (value = "/user/i_forget_password" ) @ResponseBody public Map forgetPass(HttpServletRequest request,String userName){ Users users = userService.findUserByName(userName); Map map = new HashMap<String ,String >(); String msg = "" ; if (users == null ){ //用戶名不存在 msg = "用戶名不存在,你不會忘記用戶名了吧?" ; map.put( "msg" ,msg); return map; } try { String secretKey= UUID.randomUUID().toString(); //密鑰 Timestamp outDate = new Timestamp(System.currentTimeMillis()+ 30 * 60 * 1000 ); //30分鐘后過期 long date = outDate.getTime()/ 1000 * 1000 ; //忽略毫秒數(shù) users.setValidataCode(secretKey); users.setRegisterDate(outDate); userService.update(users); //保存到數(shù)據(jù)庫 String key = users.getUserName()+ "$" +date+ "$" +secretKey; String digitalSignature = MD5.MD5Encode(key); //數(shù)字簽名 String emailTitle = "有方云密碼找回" ; String path = request.getContextPath(); String basePath = request.getScheme()+ "://" +request.getServerName()+ ":" +request.getServerPort()+path+ "/" ; String resetPassHref = basePath+ "user/reset_password?sid=" +digitalSignature+ "&userName=" +users.getUserName(); String emailContent = "請勿回復本郵件.點擊下面的鏈接,重設密碼<br/><a href=" +resetPassHref + " target='_BLANK'>點擊我重新設置密碼</a>" + "<br/>tips:本郵件超過30分鐘,鏈接將會失效,需要重新申請'找回密碼'" +key+ "\t" +digitalSignature; System.out.print(resetPassHref); SendMail.getInstatnce().sendHtmlMail(emailTitle,emailContent,users.getEmail()); msg = "操作成功,已經發(fā)送找回密碼鏈接到您郵箱。請在30分鐘內重置密碼" ; logInfo(request,userName, "申請找回密碼" ); } catch (Exception e){ e.printStackTrace(); msg= "郵箱不存在?未知錯誤,聯(lián)系管理員吧。" ; } map.put( "msg" ,msg); return map; } |
找回鏈接已經發(fā)到郵箱了。進入郵箱點開鏈接
以下為鏈接檢驗代碼,驗證通過 跳轉到修改密碼界面,否則跳轉到失敗界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@RequestMapping (value = "/user/reset_password" ,method = RequestMethod.GET) public ModelAndView checkResetLink(String sid,String userName){ ModelAndView model = new ModelAndView( "error" ); String msg = "" ; if (sid.equals( "" ) || userName.equals( "" )){ msg= "鏈接不完整,請重新生成" ; model.addObject( "msg" ,msg) ; logInfo(userName, "找回密碼鏈接失效" ); return model; } Users users = userService.findUserByName(userName); if (users == null ){ msg = "鏈接錯誤,無法找到匹配用戶,請重新申請找回密碼." ; model.addObject( "msg" ,msg) ; logInfo(userName, "找回密碼鏈接失效" ); return model; } Timestamp outDate = users.getRegisterDate(); if (outDate.getTime() <= System.currentTimeMillis()){ //表示已經過期 msg = "鏈接已經過期,請重新申請找回密碼." ; model.addObject( "msg" ,msg) ; logInfo(userName, "找回密碼鏈接失效" ); return model; } String key = users.getUserName()+ "$" +outDate.getTime()/ 1000 * 1000 + "$" +users.getValidataCode(); //數(shù)字簽名 String digitalSignature = MD5.MD5Encode(key); System.out.println(key+ "\t" +digitalSignature); if (!digitalSignature.equals(sid)) { msg = "鏈接不正確,是否已經過期了?重新申請吧" ; model.addObject( "msg" ,msg) ; logInfo(userName, "找回密碼鏈接失效" ); return model; } model.setViewName( "user/reset_password" ); //返回到修改密碼的界面 model.addObject( "userName" ,userName); return model; } |
補充1:Timestamp類型對象在保存到數(shù)據(jù)的時候 毫秒精度會丟失。比如:2013-10-08 10:29:10.234 存到mysql數(shù)據(jù)庫的時候 變成 2013-10-08 10:29:10.0。時間變得不相同了,sid 匹配的時候不會相等。 所以我做了忽略精度的操作。
補充2:解決linux下面title中文亂碼
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
mailMessage.setSubject(MimeUtility.encodeText(mailInfo.getSubject(), "UTF-8", "B")); //解決linux郵件title亂碼
補充3:怎么不直接把sid插入到user表呢。驗證的時候直接比較sid就ok了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。