This document is about: PUN 1
SWITCH TO

PUN Classic (v1)、PUN 2 和 Bolt 處於維護模式。 PUN 2 將支援 Unity 2019 至 2022,但不會添加新功能。 當然,您所有的 PUN & Bolt 專案可以用已知性能繼續運行使用。 對於任何即將開始或新的專案:請切換到 Photon Fusion 或 Quantum。

Custom Authentication

默認情況下,所有應用程式都允許匿名用戶連接,沒有任何認証機制。 Photon提供了為Photon應用執行自定義認証的選項。

Photon的自定義認証很靈活。 它支持公認的第三方認証供應商,也支持完全個性化的解決方案。

  • Facebook認証供應商,由Exit Games主持。查看此頁面
  • 自定義認証供應商,由您建立-也可由您主持。 我們通過Git倉儲提供認証提供者的執行範例。 您可以自由地區分該倉儲,並向我們發送您的拉動請求。 您可以在GitHub上找到資源。

認証流程

以下步驟描述了認証過程的一般流程。

photon cloud: custom authentication flow diagram
自定義認証流程圖
  1. 您的客戶端通過Connect()向Photon伺服器傳遞關於使用哪個認証供應商的訊息和必要的認証數據。
  2. Photon伺服器為您的應用程式獲取所需的認証供應商,並採取以下步驟之一
    • 找到認証供應商的配置 -> 認証繼續進行第3步。
    • 沒有找到認証供應商的配置 -> 客戶端將被允許連接或拒絕,這取決於您的應用程式的設置。
  3. Photon伺服器用Connect()傳遞的認証訊息來呼叫認証供應商。
    • 認証供應商在線 -> 認証繼續進行第4步。
    • 認証供應商離線 -> 客戶端將被允許連接或拒絕,這取決於相應的供應商設置。
  4. 認証供應商處理認証訊息並將結果返回給Photon伺服器。
  5. 根據認証結果,客戶端將被成功認証或被拒絕。

執行

如果您在Photon Cloud中使用facebook認証,您可以跳過這一部分。

客戶端

在客戶端,API將處理自定義認証-您只需設置相關參數和目標自定義認証服務一次。 一旦設置完畢,連接並處理最終錯誤。

例子:

C#

AuthenticationValues authValues = new AuthenticationValues();
authValues.AuthType = CustomAuthenticationType.Custom;
authValues.AddAuthParameter("user", userId);
authValues.AddAuthParameter("pass", pass);
authValues.UserId = userId; // this is required when you set UserId directly from client and not from web service
PhotonNetwork.AuthValues = authValues;
// connect

演示

Unity的PUN FreePUN+軟件包包括一個使用自定義認証的演示。

將該軟體包導入到一個新的、空的專案項目中,執行它,"Demo Hub”應該顯示可用的演示。 選擇”朋友和自定義認証演示"。

pun: custom authentication demo
PUN的”朋友和自定義授權”演示的螢幕截圖

“DemoFriends-Scene”顯示了一個簡單的用戶登錄的輸入表單。 請看”GUICustomAuth.cs”中的OnGUI()方法。 用"自定義認証登錄”按鈕的代碼設置了AuthValues並進行連接。 這將自動觸發一個自定義認証請求到您配置的服務。

注意同一文件中的呼叫返回方法OnCustomAuthenticationFailed。 它執行了對用戶認証失敗情況的反應。

伺服器端

一旦Web伺服器收到認証請求,就應該對查詢參數進行檢查和驗証。 例如,可以將憑証與存儲在數據庫中的現有憑証進行比較。

如果收到的參數丟失或無效,返回的結果應該是{ "ResultCode": 3, "Message": "Invalid parameters." }

在完成驗証後,結果應返回如下:

  • 成功:{ "ResultCode": 1, "UserId": <userId> }
  • 失敗:{ "ResultCode": 2, "Message": "Authentication failed. Wrong credentials." }

進階功能

除了對用戶進行認証外,還可以從認証供應商那裡返回額外的訊息。 為了做到這一點,用戶應該在客戶端和扮演”認証者”角色的網路服務之間建立某種協議。

向伺服器發送數據

最容易和最簡單的是”全有或全無”的策略: 選擇是否向客戶端返回靜態的變數數量。 但有些用例需要一種更復雜的方法,即網路服務根據客戶的要求”按需求”返回數據。 本小節解釋了客戶機如何向Web服務發送數據。 該數據可以是認証所需的憑証以及任何額外的參數。 額外的參數可以用來請求從伺服器端獲得的數據,並在認証響應中返回。 這非常有用,因為它節省了額外的API呼叫並簡化了登錄工作流程。

認証中使用的默認HTTP方法是GET。 因此,參數可以作為查詢字符串中的鍵/值對發送。 最終的URL將包括從客戶端設置的鍵/值對和從介面設置的鍵/值對的”聯合"。 如果在兩個地方都使用相同的鍵,那麼將只發送介面的值。 最好的做法是在介面上設置任何”敏感”的靜態值,這些值對客戶端來說應該是”不可見”的;例如:API金鑰, API版本。 另外,儀表盤的關鍵/值可以在進行中改變而不需要更新客戶端。

在一些罕見的情況下,認証可能需要大量的數據。 另一方面,大多數網路伺服器對查詢字符串中使用的字符數有限制,或者對URL的長度有閾值。 這就是為什麼Photon提供了從客戶端將HTTP方法改為POST的可能性,在C# SDKs中,這可以通過明確設置AuthenticationValues.AuthPostData字段為一個值來執行。 後者可以是stringbyte[]Dictionary<string, object>類型。 如果是Dictionary<string, object>,有效載荷將被轉換為JSON字符串,HTTP請求的Content-Type將被設置為”applicaton/json”。 在C# SDK中,AuthenticationValues類別為每個支持的類型提供設置方法。

由於這可能是一個要求或限制,所以POST方法選項也適用於任何選擇以POST方法從Web服務接收認証請求的人。

換句話說,為了發送認証參數,您可以自由地使用查詢字符串或POST數據或兩者。 下表給出了可能的組合。

AuthPostData AuthGetParameters HTTP method
null * GET
empty string * GET
string (not null, not empty) * POST
byte[] (not null, can be empty) * POST
Dictionary<string, object> (not null, can be empty) * POST (Content-Type="application/json")

向客戶返回數據

由於Photon伺服器是客戶端和網路服務之間的代理,您應該注意到可以由Photon伺服器處理的變數。

與Photon伺服器收到的所有HTTP傳入響應一樣,網路伺服器應該返回一個JSON對象,其中包括一個ResultCode和一個可選的Message。 此外,以下是Photon伺服器在認証過程中可以從網路服務期待的內容。

  • UserId: 這可以作為認証本身的一個參數,也可以從客戶端請求。 當Photon伺服器收到這個訊息時,總是會轉發給客戶端。 否則,如果AuthenticationValues.UserId最初沒有被設置,一個隨機生成的UserId將被發回給客戶端。 這將覆蓋客戶端的UserId值,此後不能再更改。 只有在ResultCode值為1的情況下才會返回這個值。 例子:{ "ResultCode": 1, "UserId": "SomeUniqueStringId" }
  • Nickname: 這可以作為認証本身的一個參數,也可以從客戶端請求。 當從網路服務返回時,它將覆蓋客戶端的Nickname值。 之後仍然可以從客戶端更新Nickname。 只有在ResultCode值為1的情況下才會返回。 例子:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "Nickname": "SomeNiceDisplayName" }
  • AuthCookie: 也稱為安全數據,是一個由Web服務返回的JSON對象,但不能從客戶端訪問,因為它將被嵌入到收到的加密令牌中。 它可以在以後通過Webhook或WebRPC HTTP請求發送。 只有在ResultCode值為1的情況下才會返回。 例子:{ "ResultCode": 1, "UserId": "SomeUniqueStringId", "AuthCookie": { "SecretKey": "SecretValue", "Check": true, "AnotherKey": 1000 } }
  • Data: JSON對象,包含任何應該返回給客戶端的額外值。 請記住,不支持嵌套數組或對象。 只有在ResultCode值為0或1的情況下才會返回。 例子:{ "ResultCode": 0, "Data": { "S": "Vpqmazljnbr=", "A": [ 1, -5, 9 ] } }

ResultCode是唯一需要的返回變數,其他都是可選的。 下表總結了網路伺服器可能返回的內容。

ResultCode Description UserId Nickname AuthCookie Data
0 Authentication incomplete, only Data returned.*
1 Authentication successful. (optional) (optional) (optional) (optional)
2 Authentication failed. Wrong credentials.
3 Invalid parameters.

*: 這對於執行OAuth 2.0或雙重驗証等可能是有用的。

###從客戶端讀取數據

下面是一個如何從響應中獲取返回值的代碼片段:

呼叫返回,以檢索在與您的自定義伺服器認証期間返回的自定義可選Data

C#

void OnCustomAuthenticationResponse(Dictionary<string, object> data)
{
    // 在這裡您可以訪問返回的數據
}

Data Types Conversion

In this section, only the type of data exchanged between Photon server and the web service is explained. For more information about data types between clients and Photon servers please refer to serialization in Photon page.

Photon Server -> Web Service

C# / .NET (Photon supported types) JavaScript / JSON
byte number
short
int
long
double
bool bool
string string
byte[] (byte array length < short.MaxValue) string (Base64 encoded)
T[] (array of supported type T, length < short.MaxValue) array
Hashtable (of supported types, count < short.MaxValue, preferably Photon implementation) object
Dictionary (keys and values of supported types, count < short.MaxValue) object
null null

Sample request data (types are concatenated)

As sent from Photon Server:<!--

JSON

{
    "(Dictionary<String,Object>)Dictionary":{
        "(Int32)dk_int":"1",
        "(String)dk_str":"dv2",
        "(Boolean)dk_bool":"True"
    },
    "(Hashtable)Hashtable":{
        "(Byte)hk_byte":"255",
        "(Object[])hk_array":[
            "(Int32)0",
            "(String)xy",
            "(Boolean)False"
        ],
        "hk_null":"null"
    },
    "null":"null",
    "(String[])string[]":[
        "PUN",
        "TB",
        "RT",
        "Bolt",
        "Chat"
    ],
    "(Byte[])byte[]":[
        "255",
        "0"
    ],
    "(Int16[])short[]":[
        "-32768",
        "32767"
    ],
    "(Int32[])int[]":[
        "-2147483648",
        "2147483647"
    ],
    "(Int64[])long[]":[
        "-9223372036854775808",
        "9223372036854775807"
    ],
    "(Single[])float[]":[
        "-3.402823E+38",
        "3.402823E+38"
    ],
    "(Double[])double[]":[
        "-1.79769313486232E+308",
        "1.79769313486232E+308"
    ],
    "(Boolean[])bool[]":[
        "True",
        "False"
    ]
}

As read by Web Service:

JSON

{
    "(object)Dictionary":{
        "dk_int":"(number)1",
        "dk_str":"(string)dv2",
        "dk_bool":"(boolean)true"
    },
    "(object)Hashtable":{
        "(number)hk_byte":"255",
        "(array)hk_array":[
            "(number)0",
            "(string)xy",
            "(boolean)false"
        ],
        "hk_null":null
    },
    "null":null,
    "(array)string[]":[
        "(string)PUN",
        "(string)TB",
        "(string)RT",
        "(string)Bolt",
        "(string)Chat"
    ],
    "byte[]":"(string)/wA=",
    "(array)short[]":[
        "(number)-32768",
        "(number)32767"
    ],
    "(array)int[]":[
        "(number)-2147483648",
        "(number)2147483647"
    ],
    "(array)long[]":[
        "(number)-9223372036854776000",
        "(number)9223372036854776000"
    ],
    "(array)float[]":[
        "(number)-3.40282347e+38",
        "(number)3.40282347e+38"
    ],
    "(array)double[]":[
        "(number)-1.7976931348623157e+308",
        "(number)1.7976931348623157e+308"
    ],
    "(array)bool[]":[
        "(boolean)true",
        "(boolean)false"
    ]
}

Web Service -> Photon Server

Here is a table that matches each JavaScript/JSON type to its equivalent one in C#/.Net :

JavaScript / JSON C# / .Net
object Dictionary
array object[] (array of objects)
number (integral) long
number (floating) double
string string
boolean bool
null (not a type) null
undefined (when sent) null

Sample response data (types are concatenated)

As sent from Web Service:<!--

JSON

{
    "(object)number": {
        "(number)MAX_VALUE": "1.7976931348623157e+308",
        "(number)MIN_VALUE": "5e-324"
    },
    "(object)object": {
        "(string)string": "xyz",
        "null": null,
        "(boolean)bool": "false",
        "(undefined)undefined": "undefined",
        "(number)float": "-3.14",
        "(number)integer": "123456"
    },
    "(array)array": [
        "(string)xyz",
        "(number)0",
        "(boolean)true",
        null,
        "(undefined)undefined"
    ]
}

As read from Photon Server:<!--

JSON

{
    "(Dictionary<String,Object>)number":{
        "(Double)MAX_VALUE":"1.79769313486232E+308",
        "(Double)MIN_VALUE":"4.94065645841247E-324"
    },
    "(Dictionary<String,Object>)object":{
        "(String)string":"xyz",
        "null":"null",
        "(Boolean)bool":"False",
        "(Double)float":"-3.14",
        "(Int64)integer":"123456"
    },
    "(Object[])array":[ 
        "(String)xyz",
        "(Int64)0",
        "(Boolean)True",
        "null",
        "null"
    ]
}

問題排除

當自定義認証失敗時,會觸發以下呼叫返回:

C#

void OnCustomAuthenticationFailed(string debugMessage)
{
   // `debugMessage`可能是認証供應商返回的內容。
}

如果您在介面中配置的認証URL返回一些HTTP錯誤,Photon伺服器會暫停認証呼叫一小段時間以避免一些負擔。 在配置或測試您的URL時,要考慮到這個”backoff”時間。

最佳實踐

  • 從認証供應商返回的結果應該包含一個可讀的Message,特別是在失敗的情況下。 這將為您節省很多除錯的痛苦。
  • 從介面上,設置靜態鍵/值對,這些鍵/值對不應該從客戶端設置。 這將防止在結果查詢字符串中出現重復的鍵。
  • 出於安全考慮,不要發送純文本密碼作為認証參數。
  • 建議從Photon介面上設置查詢字符串參數。 這樣您就可以檢查請求的來源。
  • 使用AuthenticationValues方法來設置參數,不要直接影響AuthGetParameters的值。 這將防止畸形的查詢字符串。

使用範例:阻止舊的客戶端版本

您可以使用自定義認証來拒絕來自使用舊版本(或意外版本)的客戶端的連接,並返回一個特定的錯誤,以便您可以要求用戶更新。 要做到這一點,您需要在自定義認証請求中發送版本。這取決於您是否想把它作為查詢字符串參數或POST數據參數。 在下面的例子中,我們將使用查詢字符串參數:

C#

string version = PhotonNetwork.gameVersion;
// string version = Application.version; // 如果您使用来自Unity的版本
PhotonNetwork.AuthValues = new AuthenticationValues();
PhotonNetwork.AuthValues = CustomAuthenticationType.Custom;
PhotonNetwork.AuthValues.AddAuthParameter("version", version); // HTTP GET
PhotonNetwork.ConnectUsingSettings(version);

如果您的自定義認証URL是https://example.com,那麼請求將被發送為https://example.com?version={version}。 從您的認証供應商的執行中,您應該獲得並比較收到的版本。 如果版本允許,返回{“ResultCode": 1 }。 如果不允許,您應該返回一個ResultCode,上面有您選擇的自定義值(不同於1),最好附帶一條訊息。 例子:{ "ResultCode": 5, "Message": "Version not allowed." }

Back to top