Реализация забыл пароль на Java

в настоящее время я реализую функцию забытого пароля в проекте Java. моя методология такова:--2-->

  1. пользователь щелкает ссылку Забыли пароль.
  2. на странице забыли пароль система предлагает пользователю ввести адрес электронной почты, который он зарегистрировал в системе.
  3. электронное письмо, содержащее ссылку для сброса пароля отправлено на указанный адрес электронной почты в шаге выше.
  4. пользователь нажимает ссылку, и он / она перенаправляется на страницы(сброс пароля), где пользователь может ввести свой новый пароль.
  5. на странице сброса пароля поле "адрес электронной почты" заполняется автоматически и не может быть изменено.
  6. затем пользователь вводит свой новый пароль и поле, связанное с адресом электронной почты в базе данных обновляется.

хотя я ограничил email address поле на странице сброса пароля от редактирования (поле только для чтения) любой может изменить url в адресной строке браузера и изменить поле адрес электронной почты.

Как запретить каждому пользователю изменять адрес электронной почты на странице сброса пароля?

6 ответов


вы должны сохранить его в БД перед отправкой электронной почты с помощью маркера:

  1. когда пользователь нажимает "отправить мне письмо с инструкциями по сбросу", вы создаете одну запись в БД с этими полями:email, token, expirationdate
  2. пользователь получает электронную почту с yourwwebsite.com/token и нажмите на него
  3. С token в URL-адресе, сервер может identify the user, проверьте, не истек ли запрос благодаря expirationdate, поместите правильную электронную почту в поле и попросите обновление пароля. Пользователь вводит новые пароли, и вы должны дать токен (hidden field в форме) + пароли к серверу. Сервер не заботится о текстовом поле для электронной почты, потому что with the token, user is identified strongly
  4. затем сервер проверяет, действителен ли токен с expirationdate (раз), проверить, если password match и если все в порядке, сохраните новый пароль! Сервер может отправить сообщение, чтобы сообщить пользователю, что пароль был изменен в связи с просьбой.

это действительно безопасно. Пожалуйста, используйте короткие время для expirationdate чтобы улучшить безопасность (например, 5 минут для меня) и использовать сильный токен (как GUID, см. комментарии)


Я согласен с ответом, данным @clement, если вам нужно самостоятельно реализовать функцию забытого пароля. Звучит как разумный и безопасный способ для реализации этого.

однако, в качестве альтернативы, если вам не нужно реализовывать его самостоятельно, я бы предложил использовать сервис, который делает это для вас, например Stormpath.

Если вы решите использовать Stormpath, код, который вызовет функциональность, будет выглядеть так в Java (с Java SDK Stormpath):

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

ваш пользователь получит электронное письмо со ссылкой типа:

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

и затем, когда пользователь нажимает на ссылку, вы проверяете и сбрасываете пароль следующим образом:

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

подробности о том, как это работает можно найти в Stormpath по сброс пароля документации.

при таком подходе вам не нужно реализовывать и поддерживать функциональность для себя, если у вас есть возможность не делать так.

Примечание: Stormpath присоединился Okta.


вы не можете ограничить адрес электронной почты для изменения пользователем.
Адрес электронной почты можно легко изменить, отредактировав исходный код в браузере, даже если вы взяли скрытый или сделать текстовое поле только для чтения.

вы можете предоставить uniq random string or token с соединением возврата и проверьте комбинацию адреса электронной почты и маркера после нажатия на ссылку сброс пароля или после запроса пользователя на сброс пароля, проверив адрес электронной почты и строку маркера в запросе с помощью адреса электронной почты и маркера строка в вашей базе данных.

Если адрес электронной почты присутствует в вашей базе данных, это означает, что адрес электронной почты действителен, если нет, чем дать сообщение, что адрес электронной почты не существует в записях пользователей.

Примечание :
Если вы используете какой-либо фреймворк или просто сервлет, лучше предоставить ссылку, чтобы вы могли проверить электронную почту и строку токена перед отображением формы сброса пароля. Если строка токена или адрес электронной почты недопустимы, чем вы можете ограничить пользователь из отправки запроса на сброс пароля и проверку после отправки запроса. Это будет более безопасным, чем проверка после отправки запроса сброса пароля.


есть два общих решения:

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".


этот вопрос был опубликован за 3 года до этого ответа... И все же я думаю, что это может быть полезно для других.

короче говоря: я полностью согласен с вашим потоком. Выглядит очень защищенным, и ваш единственный открытый конец также имеет смысл - как вы можете убедиться, что никто не меняет имя пользователя, и тем самым может установить новый пароль для него.

Мне меньше нравится идея временного хранения вещей-это DB (как предполагает принятый ответ).

идея, о которой я думал о том, чтобы подписать данные в ссылке, которая отправляется пользователю. Затем, когда пользователь щелкает ссылку и сервер получает вызов, сервер также получает зашифрованную часть и может проверить, что данные не были затронуты.

кстати (здесь идет "продвижение"): я реализовал проект JAVA для этих случаев использования (также "создать учетную запись", "изменить пароль" и т. д.). Это бесплатно на GitHub открытым исходным кодом. Он прекрасно отвечает на ваш вопрос... реализовано на Java, поверх Spring Безопасность.

есть объяснение всему (и если что - то отсутствует-дайте мне знать...)

посмотрите:https://github.com/OhadR/oAuth2-sample/tree/master/authentication-flows

посмотреть демо здесь.

существует также клиентское веб-приложение, которое использует auth-потоки, с README со всеми объяснениями:https://github.com/OhadR/Authentication-Flows


если вы ищете полный код для реализации забытого пароля, здесь я делюсь своим кодом. Поместите ссылку там, где вам нужно.

<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 и конкатенация с отправленным временем и шифрование и отправить его в виде строки запроса по ссылке в письме. Таким образом, электронная почта будет отправлена и ссылка на Пароль будет отправлена вместе с хэш-ключом. Теперь, когда пользователь нажимает на ссылку, они отправляются в reset_password.jsp и следующее -