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部份的重設密碼功能,只要相互組合,修正程式的驗證流程,這個小節會相對輕鬆許多。