English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
最近、システムを開発していて、忘れられたパスワードをメールで再設定する必要があるという要件がありました。現在のシステムでは、登録時にメールを強制的に入力させることで、メールをバインドしてパスワードを再設定できるようにしています。Javaでメールを送信する機能については言及しませんが、パスワードの再設定に焦点を当てます。
他者のアイデアを参考に:メールを送信→メール内のURLをリクエスト→URLを確認→{確認成功でパスワードを変更、確認失敗で失敗ページに遷移}
重要なのは、このurlをどのように生成し、どのように解析するかです。
注意すべきことは、一度のurlは一度だけパスワードを変更できることです、同じアカウントが複数のメールを送信した場合、最後のメールのurlのみが有効です
暗号化は偽造攻撃を防ぐことができます、一度のurlは一度だけ確認できます、ユーザーにバインドされています。urlの生成: 随機なキーを生成するためにUUIDを使用できます。
数字署名 = MD5(ユーザー名+'$'+有効期限+‘$'+キーkey)
データベースのフィールド(ユーザー名(主キー),キーkey,有効期限)
urlパラメータ(ユーザー名,数字署名) ,キーkeyの生成:ユーザーがパスワードを再設定するたびに、そのユーザーにキーkeyを生成する。
url example: http://localhost:8080/user/reset_password#63;sid=D622D6A23FBF86FFE696B593D55351A54AEAEA77&userName=test4
生成过期时间,生成数字签名,生成url,发送邮件. saveOrUpdate(用户名,密钥key,过期时间)
以下为springMvc代码
@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 = "ユーザー名が存在しません、ユーザー名を忘れたんですか?"63;"; 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; //ミリ秒を無視 users.setValidataCode(secretKey); users.setRegisterDate(outDate); userService.update(users); //データベースに保存 String key = users.getUserName()+"$"+date+"$"+secretKey; String digitalSignature = MD5.MD5Encode(key); //デジタル署名 String emailTitle = "有方云パスワードリセット"; String path = request.getContextPath(); String basePath = request.getScheme();+://"+request.getServerName();+:"+request.getServerPort();+path+"/"; String resetPassHref = basePath;+"user/reset_password#63;sid="+digitalSignature;+"&userName="+users.getUserName(); このメールには返信しないでください。以下のリンクをクリックしてパスワードを再設定してください<br/><a href="+resetPassHref; +" target='_BLANK'>パスワードを再設定するためにクリックしてください</a>" + "<br/このメールは30分後にリンクが無効になりますので、再びパスワードリセットをリクエストしてください。+key;+"\t"+digitalSignature; System.out.print(resetPassHref); SendMail.getInstatnce().sendHtmlMail(emailTitle, emailContent, users.getEmail()); 操作成功、パスワードリセットのリンクをあなたのメールアドレスに送信しました。リンクは0分後に無効になりますので、再びリクエストしてください。30分以内にパスワードを再設定してください"; logInfo(request,userName,"申请找回密码"); } e.printStackTrace(); msg="メールが存在しませんか?未知のエラー、管理者に連絡してください。"; } map.put("msg",msg); return map; }
找回链接已经发到邮箱了。进入邮箱点开链接
以下为链接检验代码,验证通过 跳转到修改密码界面,否则跳转到失败界面
@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(); //デジタル署名 String digitalSignature = MD5.MD5Encode(key); System.out.println(key+"\t"+digitalSignature); if(!digitalSignature.equals(sid)) { msg = "リンクが不正です、期限が切れていませんか?"63;再申請してください"; model.addObject("msg",msg) ; logInfo(userName,"パスワードリセットリンクが無効"); return model; } model.setViewName("user/reset_password"); //変更パスワードの画面に戻ります model.addObject("userName",userName); return model; }
補足1:Timestamp 型オブジェクトがデータに保存される際に、ミリ秒精度が失われます。例えば、2013-10-08 10:29:10.234 mysql データベースに保存するときに、次のように変わります 2013-10-08 10:29:10.0。時間が異なります、sid が一致する時は等しくなりません。したがって、精度を無視する操作を行いました。
補足2:linux 以下のタイトルの中文乱码を解決する
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
mailMessage.setSubject(MimeUtility.encodeText(mailInfo.getSubject(), "UTF"-8"B" //linux メールタイトルの乱码を解決する
補足3:なぜ直接 sid を user テーブルに挿入しないのですか。確認時に sid を比較するだけで大丈夫です。
これで本文のすべてが終わりました。皆様の学習に役立てば幸いです。また、呐喊教程を多くのサポートをお願いします。
声明:本文の内容はインターネットから取得しており、著作権者はすべての権利を保有しています。コンテンツはインターネットユーザーによって自発的に貢献し、アップロードされました。当サイトは所有権を持ちません。また、人工編集は行われていません。著作権に関する問題がある場合は、以下のメールアドレスにご連絡ください:notice#oldtoolbag.com(メール送信時は、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認がとりたいと、当サイトは直ちに侵害する疑いのある内容を削除します。)