註冊輔助-基礎概念

註冊輔助-基礎概念

註冊輔助系統的運作方式是,將其插入至伺服器擴展中,並且增加新的指令,讓客戶端能夠呼叫並且存取該服務。

簡單來說,使用這個元件需要三個步驟:

1.在應用程式區域(application's Zone)中配置DBManager,這樣才能夠與你的本地資料庫(local DB)進行溝通。

2.在擴展的init()方法中實例化該元件。

3.根據你的需要來設置元件。

注意:在整個教程當中我們使用MySQL資料庫做為參考,你也可以使用任何其他的資料庫引擎,像是MSSQL, MS Acces, Postgres, HSQLDB, Derby, 等等。這裡所說的元件是透過JDBC來運作,確保了移植到其他平台的可攜性。

>>簡單的使用案例

用個簡單的例子來看:我們正在推出一個全新的遊戲,需要為玩家弄個簡單的註冊系統。首先將本地的MySQL安裝好,當然相似的資料庫產品也是可以的。

然後是配置好SmartFoxServer 2X的資料庫設定,打開AdminTool,選擇Zone Configurator再選擇 Database Manager(細節請參考documentation page)。

>> 設定好資料庫表格

為了簡單起見,我們建立一個基本的資料庫表格,稱之為user,結構如下:

field name type length index
id int -- primary, autoincrement
username varchar 30 --
password varchar 30 --
email varchar 30 --
 

我們使用三個字串欄位來保存玩家的必要資訊(name, password and email),再加上一個整數的id欄位,作為主要索引(primary index)。

>>在擴展中使用元件

下面是在主要擴展類別中用來啟動註冊輔助元件的程式碼。

public class MyGameExtension extends SFSExtension
{
    private SignUpAssistantComponent suac;
     
    @Override
    public void init()
    {
        suac = new SignUpAssistantComponent();
        addRequestHandler(SignUpAssistantComponent.COMMAND_PREFIX, suac);
    }
     
    @Override
    public void destroy()
    {
        super.destroy();
    }
}

這看起來很簡單,不是嗎?

在我們討論這背後到底發生什麼事之前,先來看看在客戶端中用來送出註冊資料的程式碼。

請註意,這程式碼是用ActionScript3寫成的,但它可以很容易變換成其他SFS2X API所支援的語言。

// The SignUp extension command
var CMD_SUBMIT:String = "$SignUp.Submit";
 
var sfs:SmartFox = new SmartFox()
sfs.addEventListener(SFSEvent.EXTENSION_RESPONSE, onExtensionResponse)
 
// Send the registration data
private function sendSignUpData(evt:Event):void
{
    var sfso:SFSObject = new SFSObject();
    sfso.putUtfString("username", tf_username.text);
    sfso.putUtfString("password", tf__password.text);
    sfso.putUtfString("email", tf_email.text);
     
    sfs.send(new ExtensionRequest(CMD_SUBMIT, sfso));
}
 
private function onExtensionResponse(evt:SFSEvent):void
{
    var cmd:String = evt.params["cmd"];
    var sfso:ISFSObject = evt.params["params"];
     
    if (cmd == CMD_SUBMIT)
    {
        if (params.success)
            trace("Success, thanks for registering");
        else
            trace("SignUp Error:" + params.errorMessage);
    }
}

sendSignUpData方法會從螢幕畫面中的三個文字輸入欄位取得username, password 以及 email的值。並將其包裹至SFSObject中,然後使用$SignUp.Submit命令傳送至擴展,並執行登入輔助請求處理器。

(維克:初學者大概很難理解哪裡冒出來的$SignUp.Submit命令?就我個人理解,得先從ExtensionRequest說起,ExtensionRequest其實就是個自定義的請求,第一個參數是個字串,伺服端會把這個字串當成自定義的請求,或者也可以像這裡一樣稱之為命令。在伺服端會使用

protected void addRequestHandler(java.lang.String requestId,

                                 java.lang.Class<?> theClass),

來註冊處理程序,其中requestId就是ExtensionRequest的第一個參數,而theClass就是其處理程序,當然會有些使用上的變化,詳細就要參考說明文件了。簡單地說$SignUp.Submit命令其時就只是個"$SignUp.Submit"字串而以。沒啥特別的。)

我們在onExtensionResponse方法中處理伺服端的回應,成功時會秀出成功的訊息,或只是當錯誤發生時會秀出錯誤訊息。譬如說缺少使用者名稱或者是密碼,錯誤的訊息會送至客戶端中。

神奇的地方是?

此時你可能會很想知道..ㄟ!這個元件怎麼會知道要用哪個資料庫,哪個表格,以及欄位名稱咧?

註冊輔助元件可以藉由其配置物件(configuration object)的getConfig()方法來取得完整的設定控制。可以根據你的需求來微調許多參數。

讓我們來看一下在配置物件中的一些基本參數。

property name type default value description
signUpTable String "users" The name of the sign up table to use
idField String "id" The name of the field used to store the record id
userNameField String "username" The name of the field used to store the user name
passwordField String "password" The name of the field used to store the password
emailField String "email" The name of the field used to store the email
checkForDuplicateEmails Bool true Checks for duplicate emails
checkForDuplicateUserNames Bool true Checks for duplicate user names

事實證明,我們前面提到的“神奇,其實只是我們在建立資料庫時,就使用了註冊輔助元件的預設值。所以在我們擴展程式碼中甚至不需要修改任何設定。不用說也知道,當然也可以依據自己的喜好以及資料庫表格與欄位名稱來自己定義欄位(等等更多)。

為了更清楚的表達,下面有個例子,示範如何在擴展中使用不同的資料表以及欄位名稱來配置元件。

@Override
public void init()
{
    suac = new SignUpAssistantComponent();
     
    suac.getConfig().signUpTable = "signup";
    suac.getConfig().userNameField = "user_name";
    suac.getConfig().passwordField = "user_pword";
    suac.getConfig().emailField = "user_email";
    suac.getConfig().checkForDuplicateEmails = false;
     
    addRequestHandler(SignUpAssistantComponent.COMMAND_PREFIX, suac);
}

>>更進階的使用案例

假設我們的註冊過程中,除了名稱,密碼以及email外,還需要更多的資訊。我們在user資料表中新增兩個欄位:age與 country。

field name type length index
id int -- primary, 
autoincrement
username varchar 30 --
password varchar 30 --
email varchar 30 --
age int -- --
country varchar 30 --

現在我們更新了資料庫,但不僅僅只是要填入新的欄位而以,還要進行驗證。除此之外,為了更好的安全性,還要限制密碼長度至少要8個位元。並且限制使用者名稱的最大以及最小長度。

讓我們來看看新的程式碼:

@Override
public void init()
{
    suac = new SignUpAssistantComponent();
    suac.getConfig().extraFields = Arrays.asList("country", "age");
     
    // Set limits for min/max name and password length
    suac.getConfig().minUserNameLength = 4;
    suac.getConfig().maxUserNameLength = 30;
    suac.getConfig().minPasswordLength = 8;
    suac.getConfig().maxPasswordLength = 30;
     
    // Add a pre-process plugin for custom validation
    suac.getConfig().preProcessPlugin = new ISignUpAssistantPlugin()
    {
        @Override
        public void execute(User user, ISFSObject params, SignUpConfiguration config) throws SignUpValidationException
        {
            Integer age = params.getInt("age");
            String country = params.getUtfString("country");
 
            if (age == null)
                throw new SignUpValidationException(SignUpErrorCodes.CUSTOM_ERROR, "The age is missing");
         
            if (age < 14)
                throw new SignUpValidationException(SignUpErrorCodes.CUSTOM_ERROR, "You must be at least 14 years old to access this game");
             
 
            if (country == null || country.length() < 2)
                throw new SignUpValidationException(SignUpErrorCodes.CUSTOM_ERROR, "Pleas specify your country");
        }
    };
     
     
    addRequestHandler(SignUpAssistantComponent.COMMAND_PREFIX, suac);
}

我們可以宣告任何數量的額外欄位,做法是將欄位名稱列表傳遞給配置物件的extraField屬性。每個名稱必須符合其相對應的資料庫欄位名稱。做完了這些後,註冊輔助元件就可以從具有這些欄位名稱的SFSObject中取得客戶端傳遞過來的任何值,並且將其存放至資料庫中。

(維克:客戶端按照格式把資料填到SFSObject後,傳遞給伺服端,註冊輔助元件會負責處理,並將資料放到資料庫中。)

在新程式碼的下面幾行中,我們設定了使用者名稱與密碼允許的最大與最小字元數。超過範圍將會被拒絕並且送出錯誤訊息給使用者。

接著我們繼續進行,我們要傳遞一個客製化的插件,其中包含了對於age與country欄位的特殊驗證。登入輔助元件提供了一個方便的介面,ISignUpAssistantPlugin。這個介面可以在資料寫入資料庫之前與之後實現並執行。

(維克:實現ISignUpAssistantPlugin,然後指定給suac.getConfig().preProcessPlugin 或是suac.getConfig().postProcessPlugin,這樣就可以在資料寫進之料庫之前與之後執行額外的程式碼,譬如本例中的對於age與country的驗證)

在這個例子中我們感興趣的事是,在資料寫進資料庫之前,檢查age與country是否滿足我們的條件。在這過程中,我們可以丟出SignUpValidationException 例外來中斷註冊程序,然後送回錯誤訊息給客戶端。

在上面的程式碼範例中我們利用了匿名內部類別宣告,假如你不熟悉Java語法,你可以參考這一篇外部文章,闡明了這個概念。

>> 客戶端

伺服端的範例比初始的程式碼片段多了許多,而在客戶端,程式碼與第一個例子的差異並不大。下面這段程式碼送出了一個有效的使用者註冊請求。

// The SignUp extension command
var CMD_SUBMIT:String = "$SignUp.Submit";
 
var sfs:SmartFox = new SmartFox()
sfs.addEventListener(SFSEvent.EXTENSION_RESPONSE, onExtensionResponse)
 
// Send the registration data
private function sendSignUpData(evt:Event):void
{
    var sfso:SFSObject = new SFSObject();
    sfso.putUtfString("username", "Animal");
    sfso.putUtfString("password", "drumlover");
    sfso.putUtfString("email", "animal@muppets.net");
    sfso.putInt("age", 35);
    sfso.putUtfString("country", "MuppetsLand");
     
    sfs.send(new ExtensionRequest(CMD_SUBMIT, sfso));
}
 
private function onExtensionResponse(evt:SFSEvent):void
{
    var cmd:String = evt.params["cmd"];
    var sfso:ISFSObject = evt.params["params"];
     
    if (cmd == CMD_SUBMIT)
    {
        if (params.success)
            trace("Success, thanks for registering");
        else
            trace("SignUp Error:" + params.errorMessage);
    }
}

>>密碼模式:

到目前為止,我們討論的範例中,使用者密碼都是以純文字的方式儲存在資料庫中,假如你想要以加密的方式儲存密碼,你可以透過下面的參數來變更預設值。

@Override
public void init()
{
    suac = new SignUpAssistantComponent();
    //...
    suac.getConfig().passwordMode = PasswordMode.MD5;
    //...
}

使用MD5處理過的密碼在資料庫中需要32位元來儲存。MD5密碼是相當安全的,並且不容易進行逆向工程,雖然它依然存在著破解的方法。一般來說,以字母與數字組合而成的密碼是相當難以破解的。

(維克:使用MD5時因為很難逆推,所以使用者在忘記密碼時,只能重新給出密碼,而無法取得原始密碼。密碼儲存在伺服端資料庫中其實不容易被取得,但使用MD5可以確保在資料庫外洩時依然能保持相對的安全,因為密碼難以逆推,所以使用者帳號就不容易被盜取,但是!!~~~MD5出現其實有段時間了,仿間其實有許多對照表可以在短時間內強行破解,實際做法就不說了,簡單地說,MD5雖然較明碼密碼安全,但在進行MD5編碼時最好不要只是對使用者給出的密碼編碼,一般建議對密碼加上前綴與後綴詞,將編碼前的字串拉長,可以增加破解難度。)

假如你計劃在伺服端中儲存雜湊密碼(hashed passwords),在登入時,就會需要在客戶端中預先對你的密碼進行雜湊處理。這點很容易做到,只要透過PasswordUtil類別的幫忙就好。下面是個例子示範其運作的方式:

var userName:String = "testName";
var userPass:String = "testPass";
 
var md5Pass:String = PasswordUtil.md5Password(userPass);
sfs.send(new LoginRequest(userName, md5Pass, sfs.config.zone)

>>結語:

到目前為止我們已經學會如何配置基本的設定,如何映對客製化資料庫欄位,微調內件驗證與提供我們自己的客製化邏輯。

我們現在可以學習如何認證Email與使用者帳號激活,這是這份教學中的下個主題。

選擇你下個方向:

 
 

  按個讚!~支持本站!~

FB推薦載入中