Ch12-2 : Edit Password

[Edit Passowrd]
密碼修改分成兩個不同方式,前一個小節介紹到的是,尚未登入的忘記密碼功能,另外一個則是登入後,修改密碼的部份。在修改密碼的部份,需要特別注意的地方是,使用者必須是在登入狀態中,才可以進行修改密碼,這後我們就要利用到Ch11所寫好的登入驗證AuthCheck來幫助我們檢驗使用者是否在登入狀態中。以下介紹修改密碼如何去實作。


app.pojo.web.signup.request.EditPasswordRequest.java
新增修改密碼時,需要的request

package pojo.web.signup.request;

/**
 * 修改密碼表單請求
 */
public class EditPasswordRequest {

  private String oldPassword;

  private String password;

  private String retypePassword;


  public String getOldPassword() {
    return oldPassword;
  }

  public void setOldPassword(String oldPassword) {
    this.oldPassword = oldPassword;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getRetypePassword() {
    return retypePassword;
  }

  public void setRetypePassword(String retypePassword) {
    this.retypePassword = retypePassword;
  }

}

app.utils.session.Utils_Session.java
在修改密碼部份,當使用者成功修改密碼時,我們需要去移除目前Server上的Session資料,讓使用者重新登入。

/** 清除使用者Server上的Session */
public void clearServerSession(String userSessionId){
  injector().instanceOf(DefaultCacheApi.class).remove(userSessionId);
}


app.utils.mail.Utils_Email.java
使用者成功修改時,我們需要寄送修改成功信函到使用者的信箱。

public Email genEditPasswordOk(Member member){
  Format formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
  String time = formatter.format(new Date());
  Email email = new Email();
  email.setFrom("[email protected]");
  email.setTo(member.getEmail());
  email.setSubject("[STAR] - 密碼重新修改成功");
  email.setText("");
  email.setContent("<h2>您好 "+ member.getUsername()+"您的密碼已在"+ time +" , 修改成功。</h2> ");
  play.Logger.info("genEditPasswordOk email = " + Json.toJson(email));
  return email;
}


app.views.web.loginSignup.editPassword.scala.html
接下來,我們需要去設計修改密碼的頁面,基本上跟忘記密碼類似,只需要稍微調整內容即可。

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>修改密碼</title>

    @views.html.web.headerLibs()

    @views.html.web.loginSignup.loginSignupLibs()


  </head>

  <body>
    <div id="page-wrapper">
        <div id="select_nav_user">@views.html.web.headerNav()</div>
    </div>
    <div class="form">

      <ul class="tab-group">
        <li class="tab active"><a>修改密碼</a></li>
      </ul>
      <div class="tab-content">
        <div id="Form" > 
          <form action="@controllers.routes.WebController.editPassword.url" method="post" id="editPasswordForm">
            <div class="field-wrap">
                <label class="lable-field-wrap">原始密碼</label>
                <input type="password" required autocomplete="off" name="oldPassword"/>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">新密碼</label>
                <input type="password" required autocomplete="off" name="password"/>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">確認密碼</label>
                <input type="password" required autocomplete="off" name="retypePassword"/>
                @if(flash.containsKey("error")) {
                    <span style="color:red;">@flash.get("error")</span>
                }
            </div>
            <a>
                <input type="submit" value="SUBMIT" class="button button-block">
            </a>
            </form>
        </div>
        </div><!-- tab-content -->   
    </div> <!-- /form -->
    <div id="titleBar"></div>
    <script src='@routes.Assets.versioned("javascripts/loginSignup.js")'></script>
    <script>
        $( document ).ready(function() {
            document.getElementById("editPasswordForm").reset();
        });
    </script>
  </body>
</html>


app.controllers.WebController.java
接下來回到我們的Controller,開始寫主要的修改密碼功能,而驗證流程,會在doEditPassword的註解說明。

// 修改密碼頁面
@AuthCheck 
public Result editPassword(){
  return ok(editPassword.render());
}

/**
 * <pre>
 * Step 1 : 使用登入驗證AuthCheck,是否登入狀態中
 * Step 2 : 驗證表單
 * Step 3 : 驗證舊密碼是否符合
 * Step 4 : 密碼是否符合基本要求
 * OK 1 : 確認完畢,進行修改密碼
 * OK 2 : 修改密碼成功,寄送信箱
 * OK 3 : 會員更新動作,都需要記錄下來,寫入member_main_log
 * OK 4 : 清除使用者Cookie與Server Session,並重新登入 
 * </pre>
 */
//Step 1
@AuthCheck 
public Result doEditPassword(){
  // 清除暫存錯誤訊息
  flash().clear();

  // Step 2
  EditPasswordRequest request = null;
  try{
    request = formFactory.form(EditPasswordRequest.class).bindFromRequest().get();
  } catch (Exception e){
    e.printStackTrace();
    flash().put("error", "資料錯誤,請重新點選修改密碼功能連結,謝謝。0x2");
    return ok(editPassword.render());
  }

  // Step 3
  Utils_Session utilSsession = new Utils_Session();
  try {
    String memberNo = utilSsession.getUserNo();
    boolean isOldPassword = webService.checkMemberByMemberNoAndPassword(memberNo,request.getOldPassword());
    play.Logger.info("isOldPassword = " + isOldPassword);
    if(!isOldPassword){
      flash().put("error", "您的原始密碼輸入錯誤,請確認後重新輸入。0x3");
      return ok(editPassword.render());
    }
  } catch (Exception e){
    e.printStackTrace();
    flash().put("error", "系統忙碌中,請重新再次嘗試,謝謝。");
    return ok(editPassword.render());
  }


  // Step 4
  Utils_Signup utilsSignup = new Utils_Signup();
  VerificFormMessage message = utilsSignup.checkPassword(request.getPassword(), request.getRetypePassword());
  if(!"200".equals(message.getStatus())){
    flash().put("error", message.getStatusDesc()+"0x4");
    return ok(editPassword.render());
  }


  try{
    // Ok 1
    String password = request.getPassword();
    String memberNo = utilSsession.getUserNo();
    Member member = this.webService.findMemberByMemberNo(memberNo);

    int updateMemberPassword = this.webService.updateMemberPassword(memberNo , password);
    play.Logger.info("updateMemberPassword = " + updateMemberPassword );

    if(updateMemberPassword == 0){
      flash().put("error", "修改密碼錯誤,請重新修改密碼,謝謝。");
    }

    // Ok 2~3
    int isGenMemberChangeLogOk = this.webService.genMemberChangeLog(member);

    Utils_Email utils_Email = new Utils_Email();
    Email email = new Utils_Email().genEditPasswordOk(member);
    boolean isSendEditPasswordOk = utils_Email.sendMail(email);

    play.Logger.info("isEditPasswordOk = " + isSendEditPasswordOk + 
                     ",isGenMemberChangeLogOk = " + isGenMemberChangeLogOk);

  } catch(Exception e) {
    e.printStackTrace();
    flash().put("error", "系統忙碌中,請重新再次嘗試,謝謝。");
    return ok(editPassword.render());
  }

  // Ok 4 
  utilSsession.clearServerSession(request().cookies().get("sessionId").toString());
  utilSsession.clearClientCookie(response());
  flash().put("ok","您的密碼修改成功,請重新登入");

  return redirect(controllers.routes.WebController.login().url());
}


conf.routes
寫好Controller,接下來就是寫好對應routes

# http://127.0.0.1:9000/web/editPassword
GET        /web/editPassword        controllers.WebController.editPassword()

# http://127.0.0.1:9000/web/editPassword
POST    /web/editPassword        controllers.WebController.doEditPassword()


app.views.web.headerNav.scala.html
修改密碼功能,需要在登入之後,使用者的瀏覽器有Cookie時,才會去顯示,而實際進入到這個頁面時,前面寫好的WebController.EditPassword也因為增加了AuthCheck的檢核,可以驗證使用者是否在登入中使用該功能。每個Play Scala頁面,本身都會含有內建的request可以使用,我們只需要把使用request進行Cookie驗證即可。

@import utils.session.Utils_Session; var utilsSession = new Utils_Session();

<!-- Nav -->
<nav id="nav">
    <ul>
        <li id="nav_index"><a href="@controllers.routes.WebController.index.url">首頁</a></li>
        <li id="nav_user">
            <a>我的世界</a>
            <ul>
                <li><a href="@controllers.routes.WebController.login.url">登入</a></li>
                @if(utilsSession.isClinetHaveCookie(request)){
                    <li><a href="@controllers.routes.WebController.editPassword.url">修改密碼</a></li>
                }
                <li><a href="@controllers.routes.WebController.logout.url">登出</a></li>
            </ul>
        </li>
        <li id="nav_signup">
            <a href="@controllers.routes.WebController.signup.url">註冊</a>
            <ul>
                <li><a href="@controllers.routes.WebController.resendAuthEmail.url">重發認證信</a></li>
                <li><a href="@controllers.routes.WebController.forgotPassword.url">忘記密碼</a></li>
            </ul>
        </li>
    </ul>
</nav>
<script>
    <!-- 來偵測,外面的Div被誰使用到,而去增加class current屬性-->
    $(document).ready(function() {
      if ( $('#select_nav_index').length ){
          $('#nav_index').addClass('current');
      }
      if ( $('#select_nav_user').length ){
          $('#nav_user').addClass('current');
      }
      if ( $('#select_nav_signup').length ){
          $('#nav_signup').addClass('current');
      }
    });
</script>
<a href="index.html" id="logo">Star</a>


app.views.web.loginSignup.login.scala.html
修正登入頁面,新增flash取得ok的訊息提示。

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>登入</title>

    @views.html.web.headerLibs()

    @views.html.web.loginSignup.loginSignupLibs()


  </head>

  <body>
    <div id="page-wrapper">
        <div id="select_nav_user">@views.html.web.headerNav()</div>
    </div>
    <div class="form">

      <ul class="tab-group">
        <li class="tab active"><a href="#">登入</a></li>
      </ul>
      <div class="tab-content">
        <div id="login" >   

          <form action="@controllers.routes.WebController.login.url" method="post" id="loginForm">
            <div class="field-wrap">
                <label class="lable-field-wrap">電子信箱</label>
                <input type="email" required autocomplete="off" name="email"/>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">密碼</label>
                <input type="password" required autocomplete="off" name="password"/>
                @if(flash.containsKey("errorLogin")) {
                    <span style="color:red;">@flash.get("errorLogin")</span>
                }
                @if(flash.containsKey("ok")) {
                    <span style="color:green;">@flash.get("ok")</span>
                }
            </div>
            <input type="hidden" name="role" value="MEMBER"/>
             <ul>
                <li class="home"><a href="@controllers.routes.WebController.index.url">回首頁</a></li>
                <li class="forgot"><a href="@controllers.routes.WebController.forgotPassword.url">忘記密碼?</a></li>
             </ul>
            <a>
                <input type="submit" value="Login" class="button button-block">
            </a>
            </form>
        </div>
        </div><!-- tab-content -->   
    </div> <!-- /form -->
    <div id="titleBar"></div>
    <script src='@routes.Assets.versioned("javascripts/loginSignup.js")'></script>
    <script>
        $( document ).ready(function() {
            document.getElementById("loginForm").reset();
        });
    </script>
  </body>
</html>

[Test Case]
CASE 1 : 使用登入驗證AuthCheck,是否登入狀態中

CASE 2 : 驗證表單

CASE 3 : 驗證舊密碼是否符合

CASE 4 : 驗證密碼是否符合要求

當全部驗證都通過之後,會進行以下流程
1 : 確認完畢,進行修改密碼 2 : 修改密碼成功,寄送信箱 3 : 會員更新動作,都需要記錄下來,寫入member_main_log 4 : 清除使用者Cookie與Server Session,並重新登入
伺服器上的log訊息。

修改密碼成功後,導回登入頁面,重新登入。

修改密碼成功信件。

member_main_log會員更改紀錄。

member_main會員主檔密碼更新。


[Final]
這個小節的修改密碼功能,就算是完成了,主要利用到Ch11的登入驗證與Ch12-2部份的重設密碼功能,只要相互組合,修正程式的驗證流程,這個小節會相對輕鬆許多。

results matching ""

    No results matching ""