有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Java中实现忘记密码功能

我目前正在一个Java项目中实现忘记密码功能。我的方法是

  1. 用户单击忘记密码链接
  2. 在忘记密码页面中,系统提示用户输入他/她在系统中注册的电子邮件地址
  3. 在上述步骤中,包含重置密码链接的电子邮件将发送到给定的电子邮件地址
  4. 用户单击链接,他/她将被重定向到一个页面(重置密码),用户可以在该页面输入新密码
  5. 在重置密码页面中,“电子邮件地址”字段将自动填写,并且无法更改
  6. 然后用户输入新密码,数据库中与电子邮件地址相关的字段将更新

虽然我已经限制了重置密码页面中的email address字段的编辑(只读字段),但任何人都可以更改浏览器地址栏中的url并更改电子邮件地址字段

如何限制每个用户在重置密码页面中更改电子邮件地址


共 (6) 个答案

  1. # 1 楼答案

    如果你必须自己实现忘记密码功能,我同意@clement给出的答案。听起来这是一种合理且安全的实现方式

    然而,作为替代方案,如果您不必自己实现它,我建议使用一个为您实现这一点的服务,比如Stormpath

    如果您决定使用Stormpath,将触发该功能的代码在Java中看起来像这样(使用Stormpath的Java SDK):

    Account account = application.sendPasswordResetEmail("john.smith@example.com");
    

    您的用户将收到一封带有以下链接的电子邮件:

    http://yoursite.com/path/to/reset/page?sptoken=$TOKEN
    

    然后,当用户单击链接时,您将验证并重置密码,如下所示:

    Account account = application.resetPassword("$TOKEN", "newPassword");
    

    有关其工作原理的详细信息可以在Stormpath的password reset documentation中找到

    使用这种方法,如果您可以选择不这样做,就不必为自己实现和维护功能

    注意:Stormpath已加入Okta

  2. # 2 楼答案

    在使用令牌发送电子邮件之前,您必须将其保存在DB中:

    1. 当用户点击“向我发送一封带有重置说明的电子邮件”时,您可以在DB中创建一条记录,其中包含以下字段:emailtokenexpirationdate
    2. 用户通过yourwwebsite接收电子邮件。com/token并点击它
    3. 使用Url中的token,服务器可以identify the user,检查请求是否由于expirationdate而过期,将正确的电子邮件放入框中,并请求密码续订。用户输入新密码,您必须向服务器提供令牌(hidden field格式)+密码。服务器不关心电子邮件的文本框,因为with the token, user is identified strongly
    4. 然后服务器用expirationdate(再次)检查令牌是否仍然有效,检查password match,如果一切正常,保存新密码!服务器可以再次发送消息,通知用户密码已因请求而更改

    这真的很安全。请为expirationdate使用较短的时间来提高安全性(例如5分钟对我来说是正确的),并使用强令牌(作为GUID,请参阅注释)

  3. # 3 楼答案

    您不能限制用户更改电子邮件地址
    通过在浏览器中编辑源代码,可以轻松更改电子邮件地址,即使您已将“隐藏”或将文本框设置为只读

    您可以为uniq random string or token提供重置链接和 在点击重置密码链接后,或在用户提交重置密码请求后,通过检查请求中的电子邮件地址和令牌字符串以及数据库中的电子邮件地址和令牌字符串,检查电子邮件地址和令牌组合

    如果您的数据库中存在电子邮件地址,则表示电子邮件地址有效,如果不存在,则发送消息说明您的用户记录中不存在电子邮件地址

    注意:
    如果您使用的是任何框架或简单的servlet,那么最好提供链接,以便在显示重置密码表单之前验证电子邮件和令牌字符串。如果令牌字符串或电子邮件地址无效,则可以限制用户提交重置密码请求,并在提交请求后进行验证。它将比提交重置密码请求后进行验证更安全

  4. # 4 楼答案

    有两种常见的解决方案:

    1. Creating a new password on the server and inform user from it.
    2. Sending a unique URL to reset password.
    

    第一种解决方案有很多问题,不适合使用。这些 以下是一些原因:

    1. The new password which is created by server should be sent through an insecure channel (such as email, sms, ...) and resides in your inbox. 
    
    2. If somebody know the email address or phone number of a user who has an account at a website then then it is possible to reset user password.
    

    所以,第二种解决方案更适合使用。但是,你应该考虑以下问题:

    - The reset url should be random, not something guessable and unique to this specific instance of the reset process.
    
    - It should not consist of any external information to the user For example, a reset URL should not simply be a path such as “.../?username=Michael”. 
    
    - We need to ensure that the URL is loaded over HTTPS. No, posting to HTTPS is not enough, that URL with the token must implement transport layer 
      security so that the new password form cannot be MITM’d and the password the user creates is sent back over a secure connection.
    
    - The other thing we want to do with a reset URL is setting token's expiration time so that the reset process must be completed within a certain duration.
    
    - The reset process must run once completely. So, Reset URL can not be appilicable if the reset process is done completely once.
    

    常见的解决方案是生成一个URL来创建一个唯一的令牌,该令牌可以作为URL参数发送,它包含一个URL,例如 “重置/?id=2ae755640s15cd3si8c8i6s2cib9e14a1ae552b”

  5. # 5 楼答案

    这个问题在回答这个问题三年前就发布了。。。但我认为这可能对其他人有所帮助

    简而言之:我完全同意你的观点。看起来非常安全,你唯一的开放式终端也很有意义——你如何确保没有人更改用户名,从而为他设置新密码

    我不太喜欢暂时存储东西的想法是DB(正如公认的答案所示)

    我考虑的想法是在发送给用户的链接中对数据进行签名。然后,当用户点击链接,服务器收到呼叫时,服务器也会得到加密部分,并可以验证数据是否未被触碰

    顺便说一句(这里有一个“推广”):我已经为这些用例实现了一个JAVA项目(还有“创建帐户”、“更改密码”等)。它在GitHub上是免费的,开源的。它完美地回答了你的问题。。。在Spring Security的基础上,用Java实现

    每件事都有解释(如果遗漏了什么,请告诉我……)

    看看:https://github.com/OhadR/oAuth2-sample/tree/master/authentication-flows

    见a Demo here

    还有一个客户端web应用程序使用auth流,其自述文件包含所有解释:https://github.com/OhadR/Authentication-Flows

  6. # 6 楼答案

    如果你正在寻找实现忘记密码的完整代码,我在这里分享我的代码。 把链接放到你需要的地方

    <button> <a href="forgotpassword.jsp" style="text-decoration:none;">Forgot 
    Password</a></button>
    

    下面是我的forgotpassword.jsp页面

     <form id="register-form" role="form" class="form" method="post" 
     action="mymail_fp.jsp">
        <h3>Enter Your Email Below</h3>
       <input id="email" name="email" placeholder="Email address" class="form- 
       control"  type="email" required autofocus>
      <input name="recover-submit" class="btn btn-lg btn-primary btn-block" 
       value="Get Password" type="submit">
    </form>
    

    一旦提交了电子邮件,它就会被重定向到mymail_fp.jsp页面,在那里我将电子邮件发送给用户。 下面是mymail.jsp

    <% 
    mdjavahash md = new mdjavahash();
    String smail =request.getParameter("email");
    int profile_id = 0;
    if(smail!=null)
    {
     try{
    // Register JDBC driver
    Class.forName("com.mysql.jdbc.Driver");
    
    // Open a connection
    Connection conn = 
    DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare", "root", 
    "");
    
    Statement stmt = conn.createStatement();
    
     String sql1;
     sql1="SELECT  email FROM profile WHERE email = '"+smail+"'";
    
      ResultSet rs1=stmt.executeQuery(sql1);
    
    if(rs1.first())
    {
        String sql;
        sql = "SELECT Profile_id FROM profile where email='"+smail+"'";
         ResultSet rs2 = stmt.executeQuery(sql);
    
        // Extract data from result set
        while(rs2.next()){
           //Retrieve by column name
         profile_id  = rs2.getInt("Profile_id");
        }
    
        java.sql.Timestamp  intime = new java.sql.Timestamp(new 
        java.util.Date().getTime());
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(intime.getTime());
        cal.add(Calendar.MINUTE, 20);
        java.sql.Timestamp  exptime = new Timestamp(cal.getTime().getTime());
    
        int rand_num = (int) (Math.random() * 1000000);
        String rand = Integer.toString(rand_num);
        String finale =(rand+""+intime); // 
        String hash = md.getHashPass(finale); //hash code
    
        String save_hash = "insert into  reset_password (Profile_id, hash_code, 
       exptime, datetime) values("+profile_id+", '"+hash+"', '"+exptime+"', 
       '"+intime+"')";
        int saved = stmt.executeUpdate(save_hash);
        if(saved>0)
        {
      String link = "http://localhost:8080/Infoshare/reset_password.jsp";     
      //bhagawat till here, you have fetch email and verified with the email 
     from 
      datbase and retrived password from the db.
        //-----------------------------------------------
    String host="", user="", pass=""; 
    host = "smtp.gmail.com"; user = "example@gmail.com"; 
    //"email@removed" // email id to send the emails 
    pass = "xxxx"; //Your gmail password 
    String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory"; 
    String to = smail;  
    String from = "example@gmail.com";  
    String subject = "Password Reset"; 
     String messageText = " Click <a href="+link+"?key="+hash+">Here</a> To 
      Reset 
      your Password. You must reset your password within 20 
      minutes.";//messageString; 
       String fileAttachment = ""; 
       boolean WasEmailSent ; 
      boolean sessionDebug = true; 
      Properties props = System.getProperties(); 
      props.put("mail.host", host); 
      props.put("mail.transport.protocol.", "smtp"); 
      props.put("mail.smtp.auth", "true"); 
      props.put("mail.smtp.", "true"); 
      props.put("mail.smtp.port", "465"); 
      props.put("mail.smtp.socketFactory.fallback", "false"); 
      props.put("mail.smtp.socketFactory.class", SSL_FACTORY); 
      Session mailSession = Session.getDefaultInstance(props, null); 
      mailSession.setDebug(sessionDebug); 
      Message msg = new MimeMessage(mailSession); 
      msg.setFrom(new InternetAddress(from)); 
      InternetAddress[] address = {new InternetAddress(to)}; 
      msg.setRecipients(Message.RecipientType.TO, address); 
      msg.setSubject(subject); 
      msg.setContent(messageText, "text/html");  
      Transport transport = mailSession.getTransport("smtp"); 
      transport.connect(host, user, pass);
        %>
     <div class="alert success" style="padding: 30px; background-color: grey; 
      color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10% 
     5% 
    15% 20%;">
     <a href="forgotpassword.jsp"> <span class="closebtn" style="color: white; 
    font-weight: bold; float: right; font-size: 40px; line-height: 35px; cursor: 
    pointer; transition: 0.3s;">&times;</span> </a> 
     <h1 style="font-size:30px;">&nbsp;&nbsp; <strong>Check Your Email. Link To 
    Reset Your Password Is Sent To : <%out.println(" "+smail); %></strong>  
    </h1>
     <center><a href="forgotpassword.jsp"><h2><input type="button" value="OK"> 
    </h2></a></center>
    </div>
    <%
    try { 
    transport.sendMessage(msg, msg.getAllRecipients()); 
    WasEmailSent = true; // assume it was sent 
    } 
    catch (Exception err) { 
    WasEmailSent = false; // assume it's a fail 
    } 
     transport.close();
        //-----------------------------------------------
     }  
    }   
    
     else{
        %>
        <div class="alert success" style="padding: 30px; background-color: grey; 
     color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10% 
     5% 15% 20%;">
         <a href="forgotpassword.jsp"> <span class="closebtn" style="color: 
     white; font-weight: bold; float: right; font-size: 40px; line-height: 35px; 
     cursor: pointer; transition: 0.3s;">&times;</span> </a> 
         <h1 style="font-size:30px;">&nbsp;&nbsp; <strong>There Is No Email As 
     Such <%out.println(" "+smail); %></strong>Try Again  </h1>
         <center><a href="forgotpassword.jsp"><h2><input type="button" 
     value="OK"></h2></a></center>
        </div>
        <%      
     }  
    
    stmt.close();
    rs1.close();
    conn.close();
    }catch(SQLException se){
    //Handle errors for JDBC
    se.printStackTrace();
    }catch(Exception e){
    //Handle errors for Class.forName
    e.printStackTrace();
    }
    }
     else{
        %>
     <div class="alert success" style="padding: 30px; background-color: grey; 
     color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10% 
     5% 15% 20%;">
      <a href="forgotpassword.jsp"> <span class="closebtn" style="color: white; 
      font-weight: bold; float: right; font-size: 40px; line-height: 35px; 
     cursor: 
     pointer; transition: 0.3s;">&times;</span> </a> 
     <h1 style="font-size:30px;">&nbsp;&nbsp; <strong>Please Enter The Valid 
     Email Address</strong>  </h1>
     <center><a href="forgotpassword.jsp"><h2><input type="button" value="OK"> 
     </h2></a></center>
     </div>
      <%    
      }
      %> 
    

    现在我在这里做的是,在向用户发送电子邮件之前,我保存发送时间、过期时间、生成0到1000000的随机数,并与发送时间连接,加密is,并将其作为电子邮件链接中的查询字符串发送。因此,电子邮件将被发送,密码链接将与散列密钥一起发送。现在,当用户点击链接时,他们会被发送来重置密码。jsp,下面是reset_password.jsp

    <%
    String hash = (request.getParameter("key"));
    
    java.sql.Timestamp  curtime = new java.sql.Timestamp(new 
    java.util.Date().getTime());
    
    int profile_id = 0;
    java.sql.Timestamp exptime;
    
    try{
    // Register JDBC driver
    Class.forName("com.mysql.jdbc.Driver");
    
    // Open a connection
    Connection conn = 
    DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare", "root", 
    "");
    Statement stmt = conn.createStatement();
    
     String sql = "select profile_id, exptime from reset_password where 
     hash_code ='"+hash+"'";
     ResultSet rs = stmt.executeQuery(sql);
     if(rs.first()){
     profile_id = rs.getInt("Profile_id");  
     exptime = rs.getTimestamp("exptime");
    
      //out.println(exptime+"/"+curtime);
      if((curtime).before(exptime)){        
          %>
          <div class="container">
           <form class="form-signin" action="update_reset.jsp" method="Post"> 
          <br/><br/>
             <h4 class="form-signin-heading">Reset Your Password Here</h4>
             <br> 
              <text style="font-size:13px;"><span class="req" 
            style="color:red">* </span>Enter New Password</text>
             <input type="password" id="inputPassword" name="newpassword" 
           class="form-control" placeholder="New Password" required autofocus>
             <br>
              <text style="font-size:13px;"><span class="req" 
             style="color:red">* </span>Enter New Password Again</text>
             <input type="password" id="inputPassword" name="confirmpassword" 
             class="form-control" placeholder="New Password Again" required>
    
              <input type="hidden" name="profile_id" value=<%=profile_id %>>
            <br>
             <button class="btn btn-lg btn-primary btn-block" 
        type="submit">Reset Password</button>
           </form>
         </div> <!-- /container -->
        <% } 
        else{
            %>
            <div class="alert success" style="padding: 30px; background-color: 
       grey; color: white; opacity: 1; transition: opacity 0.6s; width:50%; 
      margin: 10% 5% 15% 20%;">
                 <a href="forgotpassword.jsp"> <span class="closebtn" 
       style="color: white; font-weight: bold; float: right; font-size: 40px; 
       line-height: 35px; cursor: pointer; transition: 0.3s;">&times;</span> 
       </a> 
                 <h1 style="font-size:30px;">&nbsp;&nbsp; The Time To Reset 
      Password Has Expired.<br> &nbsp;&nbsp; Try Again </h1>
                 <center><a href="forgotpassword.jsp"><h2><input type="button" 
         value="OK"></h2></a></center>
            </div>
           <%       
           }    
         }
       else{
        %>
        <div class="alert success" style="padding: 30px; background-color: grey; 
       color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 
        10% 5% 15% 20%;">
             <a href="forgotpassword.jsp"> <span class="closebtn" style="color: 
          white; font-weight: bold; float: right; font-size: 40px; line-height: 
           35px; cursor: pointer; transition: 0.3s;">&times;</span> </a> 
             <h1 style="font-size:30px;">&nbsp;&nbsp; The Hash Key DO Not Match. 
                <br/> &nbsp;&nbsp;&nbsp;Try Again!! </h1>
             <center><a href="forgotpassword.jsp"><h2><input type="button" 
             value="OK"></h2></a></center>
            </div>
        <%
        }
       // Clean-up environment
       rs.close();
       stmt.close();
       conn.close();
      }catch(SQLException se){
      //Handle errors for JDBC
      se.printStackTrace();
     }catch(Exception e){
      e.printStackTrace();
      }
    %> 
    

    在这个页面中,我获取散列键并与数据库散列键进行比较,结果为真,然后获取过期时间并与当前时间进行比较。如果重置密码的时间尚未到期,则我会显示重置密码的表单,否则我会抛出错误消息。如果时间还没有到期,我会显示表格,当表格提交时,它会被重定向到update_reset.jsp,下面是我的update_reset.jsp页面

     <%  
     mdjavahash md = new mdjavahash();
     String profile_id= request.getParameter("profile_id");
     String np= request.getParameter("newpassword");
     String cp = request.getParameter("confirmpassword");
     //out.println(np +"/"+ cp);
    
     if( np.equals(" ") || cp.equals(" ")){%>
     <div class="alert success" style="padding: 30px; background-color: grey; 
     color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10% 
     5% 15% 20%;">
         <a href="reset_password?profile_id=<%=profile_id%>"> <span 
      class="closebtn" style="color: white; font-weight: bold; float: right; 
        font-size: 40px; line-height: 35px; cursor: pointer; transition: 
       0.3s;">&times;</span> </a> 
         <h1 style="font-size:30px;">&nbsp;&nbsp; Please Fill Both The Fields 
        </h1>
         <center><a href="reset_password?profile_id=<%=profile_id%>""><h2><input 
        type="button" value="OK"></h2></a></center>
       </div>   
       <% }
       else if(!np.equals(cp)){
        %>
        <div class="alert success" style="padding: 30px; background-color: grey; 
      color: white; opacity: 1; transition: opacity 0.6s; width:50%; margin: 10% 
      5% 15% 20%;">
             <a href="reset_password?profile_id=<%=profile_id%>"> <span 
         class="closebtn" style="color: white; font-weight: bold; float: right; 
            font-size: 40px; line-height: 35px; cursor: pointer; transition: 
                 0.3s;">&times;</span> </a> 
             <h1 style="font-size:30px;">&nbsp;&nbsp; The Two Passwords Do Not 
            Match. Try Again </h1>
             <center><a href="reset_password?profile_id=<%=profile_id%>"><h2> 
               <input type="button" value="OK"></h2></a></center>
            </div>
          <%        
         }
        else{   
          try{
            // Register JDBC driver
            Class.forName("com.mysql.jdbc.Driver");
    
            // Open a connection
            Connection conn = 
            DriverManager.getConnection("jdbc:mysql://localhost:3306/infoshare", 
          "root", "");
            // Execute SQL query
            Statement stmt = conn.createStatement();
            stmt.executeUpdate("update profile set 
           password='"+md.getHashPass(np)+"' where Profile_id="+profile_id+"");
            //response.sendRedirect("mainpage.jsp");
            %>
            <div class="alert success" style="padding: 30px; background-color: 
           grey; color: white; opacity: 1; transition: opacity 0.6s; width:65%; 
          margin: 10% 5% 15% 20%;">
             <a href="login.jsp"> <span class="closebtn" style="color: white; 
            font-weight: bold; float: right; font-size: 40px; line-height: 35px; 
             cursor: pointer; transition: 0.3s;">&times;</span> </a> 
             <h1 style="font-size:30px;">&nbsp;&nbsp; The Password Is 
                Successfully Reset.<br>&nbsp;&nbsp; Try Login With New 
                 Password</h1>
             <br><br><center><a href="login.jsp"><p style="font-size:20px"> 
                <input type="button" style="width:40px; height:35px;" 
            value="OK"></p></a> 
            </center>
               </div>                   
              <%
               stmt.close();
               conn.close();
            }catch(SQLException se){
              //Handle errors for JDBC
               se.printStackTrace();
            }catch(Exception e){
            //Handle errors for Class.forName
             e.printStackTrace();
           }    
      } 
    %>
    

    在这个页面中,我首先验证字段,然后用新密码更新数据库。虽然很长,但很有效。我在这里使用了MD5加密技术,如果你想知道怎么做,请点击链接How to Use MD5 Hash for securing Login passwords in JSP with Javascript?