Ch10-4 : Auth
[Auth]
承襲上一個小節的註冊,這個小節會說明,註冊認證信的實作,經由上一節的介紹,會Play各種操作更為熟悉,這個小節一樣切開成小功能,依序完成這些小功能後,就可以完成認證信的驗證功能了。
- Step1 : 檢查認證是否存在
- Step2 : 檢查會員是否已經認證過
- Step3 : 檢查連結是否使用過
- Step4 : 檢查是否有逾期
- Step5 : 檢查通過,開始更新與新增相關表單
- Step5.1 : 更新會員認證表單
- Step5.2 : 更新會員表單
- Step5.3 : 新增會員紀錄表單
Stop 0
回到WebController,我們基本上的認證信程式會是這個流程,下面的Step會依序講解認證步驟。
path : /app/controllers/WebController.java
/**
* <pre>
* 檢查註冊認證信連結
*
* Step1 : 檢查認證是否存在。
* Step2 : 檢查會員是否已經認證過。
* Step3 : 檢查連結是否使用過。
* Step4 : 檢查是否有逾期。
* Step5 : 檢查通過,開始更新與新增相關表單。
* Step5.1 : 更新會員認證表單
* Step5.2 : 更新會員表單
* Step5.3 : 新增會員紀錄表單
*
* </pre>
*/
public Result authMember(String auth){
// 清除暫存錯誤訊息
flash().clear();
// Step 1
MemberToken memberToken = null ;
try{
memberToken = webService.getMemberTokenData(auth , MemberTokenType.Signup.toString());
} catch(Exception e){
e.printStackTrace();
}
if(memberToken == null){
flash().put("authError", "認證連結有誤,請重新點選信中認證連結,或使用重發認證信,謝謝。");
play.Logger.warn("memberToken = " + Json.toJson(memberToken));
return ok(checkMemberAuth.render());
}
// Step 2
Member member = null;
try{
// 認證連結有資料,使用會員編號,查詢會員資料。
member = webService.findMemberByMemberNo(memberToken.getMemberNo());
} catch(Exception e){
e.printStackTrace();
flash().put("authError", "系統忙碌中,請稍後再次嘗試!");
return ok(checkMemberAuth.render());
}
if(!MemberStatus.S1.getStatus().equals(member.getStatus())){
flash().put("authError", "您的帳號,已經認證成功,不需再認證,謝謝。");
play.Logger.warn("member = " + Json.toJson(member));
return ok(checkMemberAuth.render());
}
// Step 3
boolean isUse = memberToken.getIsUse(); // 認證字串是否使用過
if (isUse){
flash().put("authError", "該連結已成功認證,不需要再次認證,謝謝。");
return ok(checkMemberAuth.render());
}
// Step 4
long dbTime = Long.parseLong(memberToken.getDbTime()); // 資料庫時間
long expiryDate = Long.parseLong(memberToken.getExpiryDate()); // 逾期時間
if(dbTime > expiryDate){
flash().put("authError", "認證時間已經逾期,請重新使用重發認證信功能謝謝。");
play.Logger.warn("dbTime = " + dbTime);
play.Logger.warn("expiryDate = " + expiryDate);
return ok(checkMemberAuth.render());
}
// Step 5
try{
int isUpdateMemberTokenOk = webService.updateMemberToken(member.getMemberNo(), MemberTokenType.Signup.toString());
int isUpdateMemberMainOk = webService.updateMemberToAuthOk(member.getMemberNo());
int isGenMemberChangeLogOk = webService.genMemberChangeLog(member);
play.Logger.info("isUpdateMemberAuthOk = " + isUpdateMemberTokenOk);
play.Logger.info("isUpdateMemberMainOk = " + isUpdateMemberMainOk);
play.Logger.info("isGenMemberChangeLogOk = " + isGenMemberChangeLogOk);
} catch(Exception e){
e.printStackTrace();
}
return ok(checkMemberAuth.render());
}
對應的網址。
path : conf/routes
# http://127.0.0.1:9000/web/authMember?auth=XXXXX
GET /web/authMember controllers.WebController.authMember(auth : String)
Step1 : 檢查認證是否存在。
當使用者點選註冊認證信回來之後,我們首先要驗證的是,認證字串是否有在我們的資料庫裡。
EX URL : http://127.0.0.1:9000/web/authMember?auth=3464f407e099ab96c9372973c428439db0fa83c0b57c1048fb4a98467db23310
path : /app/controllers/WebController.java
public Result authMember(String auth){
// 清除暫存錯誤訊息
flash().clear();
// Step 1
MemberToken memberToken = null ;
try{
memberToken = webService.getMemberTokenData(auth , MemberTokenType.Signup.toString());
} catch(Exception e){
e.printStackTrace();
}
if(memberToken == null){
flash().put("authError", "認證連結有誤,請重新點選信中認證連結,或使用重發認證信,謝謝。");
play.Logger.warn("memberToken = " + Json.toJson(memberToken));
return ok(checkMemberAuth.render());
}
...
}
在Controller檢查認證連結memberAuth = webService.getMemberAuthData(auth)時,我們需要新增MemberAuth類別,來對應註冊認證資料。
path : /app/pojo/web/MemberAuth.java
package pojo.web;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MemberToken {
private String tokenString;
private String memberNo;
private String type;
private String sendDate;
private boolean isUse;
private String createDate;
private String modifyDate;
private String expiryDate;
private String dbTime;
public String getTokenString() {
return tokenString;
}
public void setTokenString(String tokenString) {
this.tokenString = tokenString;
}
public String getMemberNo() {
return memberNo;
}
public void setMemberNo(String memberNo) {
this.memberNo = memberNo;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSendDate() {
return sendDate;
}
public void setSendDate(String sendDate) {
this.sendDate = sendDate;
}
public boolean getIsUse() {
return isUse;
}
public void setIsUse(boolean isUse) {
this.isUse = isUse;
}
public String getCreateDate() {
return createDate;
}
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
public String getModifyDate() {
return modifyDate;
}
public void setModifyDate(String modifyDate) {
this.modifyDate = modifyDate;
}
public String getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(String expiryDate) {
this.expiryDate = expiryDate;
}
public String getDbTime() {
return dbTime;
}
public void setDbTime(String dbTime) {
this.dbTime = dbTime;
}
public void setUse(boolean isUse) {
this.isUse = isUse;
}
}
檢查連結時,需要查詢member_auth(會員認證資料表),是否有這個認證字串的註冊會員。我們在WebService新增好查詢的行為後,再到XML寫出查詢會員認證的SQL程式。
path : app/services/WebService.java
public interface WebService {
...
/** 驗證會員連結 */
public MemberToken getMemberTokenData(@Param("token") String token , @Param("type") String type);
...
}
查詢的SQL語法。
可以看到,我有多查詢dbTime(資料庫時間),是為了之後驗證會員認證連結時,是否有逾期,需要查詢出目前的資料庫時間,跟認證的逾期日期作為比對的依據。
<!-- 撈出會員認證資訊 -->
<select id="getMemberTokenData" parameterType="String" resultType="pojo.web.MemberToken">
SELECT *,DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') as dbTime FROM member_token WHERE tokenString=#{token} AND type=#{type}
</select>
測試網址 : http://127.0.0.1:9000/web/authMember?auth=test
檢查頁面
Step2 : 檢查會員是否已經認證過。
有時候會員已經點選過認證信件,而又不小心點選到認證信件時,這時候我們需要提醒使用者,您已經認證過了,不需要在認證了。
note : 以下是會員狀態的說明
S1("1", "尚未認證"),
S2("2", "已認證"),
S3("3", "帳號停用");
path : /app/controllers/WebController.java
...
// Step 2
Member member = null;
try{
// 認證連結有資料,使用會員編號,查詢會員資料。
member = webService.findMemberByMemberNo(memberAuth.getMemberNo());
} catch(Exception e){
e.printStackTrace();
flash().put("authError", "系統忙碌中,請稍後再次嘗試!");
return ok(checkMemberAuth.render());
}
if(!MemberStatus.S1.getStatus().equals(member.getStatus())){
flash().put("authError", "您的帳號,已經認證成功,不需再認證,謝謝。");
play.Logger.warn("member = " + Json.toJson(member));
return ok(checkMemberAuth.render());
}
...
新增會員狀態的類別。
path : app/pojo/web/MemberStatus.java
package pojo.web;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public enum MemberStatus {
S1("1", "尚未認證"),
S2("2", "已認證"),
S3("3", "帳號停用");
MemberStatus(String status, String statusDesc) {
this.status = status;
this.statusDesc = statusDesc;
}
public final String status; // 狀態代碼
public final String statusDesc; // 狀態說明
public String getStatus() {
return status;
}
public String getStatusDesc() {
return statusDesc;
}
}
WebService,新增用會員編號查詢會員資料。
path : app/services/WebService.java
public interface WebService {
...
/** 用會員編號 尋找會員資料 */
public Member findMemberByMemberNo(String memberNo);
...
}
查詢的SQL語法。
會員編號查詢會員資料。
<!-- 根據memberNo查詢會員資料 -->
<select id="findMemberByMemberNo" parameterType="String"
resultType="pojo.web.Member">
SELECT * FROM member_main WHERE memberNo = #{memberNo}
</select>
檢查畫面。
Step3 : 檢查連結是否使用過。
當認證連結,已經認證成功後,該連結會失效,需要提示使用者,已經不需要再次認證了。
path : /app/controllers/WebController.java
...
// Step 3
boolean isUse = memberToken.getIsUse(); // 認證字串是否使用過
if (isUse){
flash().put("authError", "該連結已成功認證,不需要再次認證,謝謝。");
return ok(checkMemberAuth.render());
}
...
檢查畫面。
Step4 : 檢查是否有逾期。
在Step 1撈出的會員認證資料,我們要檢查是否連結超過時間而失效了,若是超過時間,提示使用者,已經逾期。
path : /app/controllers/WebController.java
// Step 4
long dbTime = Long.parseLong(memberToken.getDbTime()); // 資料庫時間
long expiryDate = Long.parseLong(memberToken.getExpiryDate()); // 逾期時間
if(dbTime > expiryDate){
flash().put("authError", "認證時間已經逾期,請重新使用重發認證信功能謝謝。");
play.Logger.warn("dbTime = " + dbTime);
play.Logger.warn("expiryDate = " + expiryDate);
return ok(checkMemberAuth.render());
}
檢查畫面。
Step5 : 檢查通過,開始更新與新增相關表單。
當以上都檢查通過時,代表該會員符合三個條件,會員尚未認證,認證連結還可以使用,會員認證連結尚未逾期。符合以上條件後,我們會進行三個動作。
Step5.1 : 更新會員認證表單,修改已使用
Step5.2 : 更新會員表單,變成已認證狀態
Step5.3 : 新增會員紀錄表單,寫入上一次會員資料修改前的資料
path : /app/controllers/WebController.java
// Step 5
try{
int isUpdateMemberTokenOk = webService.updateMemberToken(member.getMemberNo(), MemberTokenType.Signup.toString());
int isUpdateMemberMainOk = webService.updateMemberToAuthOk(member.getMemberNo());
int isGenMemberChangeLogOk = webService.genMemberChangeLog(member);
play.Logger.info("isUpdateMemberAuthOk = " + isUpdateMemberTokenOk);
play.Logger.info("isUpdateMemberMainOk = " + isUpdateMemberMainOk);
play.Logger.info("isGenMemberChangeLogOk = " + isGenMemberChangeLogOk);
} catch(Exception e){
e.printStackTrace();
}
return ok(checkMemberAuth.render());
WebService,更新會員認證、更新會員資料成為認證狀態、新增會員修改紀錄。
path : app/services/WebService.java
public interface WebService {
...
/** 更新認證連結*/
public int updateMemberToken(@Param("memberNo") String memberNo , @Param("type") String type);
/** 更新會員資料*/
public int updateMemberToAuthOk(String memberNo);
/** 新增會員紀錄檔資料 */
public int genMemberChangeLog(@Param("member") Member member);
...
}
相關的SQL語法。
Note : 更新會員認證 : 若同個會員有多組尚未認證的資料,需要同時一起更新成使用過,以免使用者點選以前其它的認證信連結。
path : conf/services/WebService.xml
<!-- 更新會員認證-->
<update id="updateMemberToken">
UPDATE member_token SET isUse = 1 , modifyDate = DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
WHERE memberNo = #{memberNo} AND isUse = 0 AND type = #{type}
</update>
<!-- 更新會員資料 -->
<update id="updateMemberToAuthOk">
UPDATE member_main SET status = '2' , modifyDate = DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
WHERE memberNo = #{memberNo}
</update>
<!-- 會員記錄檔 -->
<insert id="genMemberChangeLog" parameterType="pojo.web.Member">
insert into
member_main_log(memberNo , email , password , username , createDate)
values
(
#{member.memberNo} ,
#{member.email} ,
#{member.password},
#{member.username} ,
DATE_FORMAT(NOW(), '%Y%m%d%H%i%s')
);
</insert>
成功認證畫面。
member_token資料更新。
member_main資料更新。
新增member_main_log會員修改紀錄。
[Final]
這個小節完成了會員認證信的部份,下一個小節,是為了因應,會員忘記去點選認證信,所設計的功能,會跟這個小節頗為類似,若完成之後,可以往下個小節前進,重發認證信功能。
如果有詳細看我的流程步驟,會發現,會員認證的Step 5其實是有問題的,因為我並沒有作任何錯誤的處理,所以特別留下這個地方當作練習,如果其中一部分失敗了,你應該怎樣去處理,這會是一個很好的練習,請嘗試修正看看。