Custom Authentication
預設下,所有應用程式都允許匿名使用者連接,並且沒有任何身份驗證機制。
Photon提供了為Photon應用程式實作自訂身份驗證的選項。
Photon的自訂身份驗證非常靈活。
它支援知名的第三方身份驗證提供者以及完全個人化的解決方案。
*自訂身份驗證提供者,由您組建,也可能由您託管。
我們透過Git存儲庫提供身份驗證提供者的範例實作。
請隨時分支存儲庫並向我們發送您的拉取請求。
您可以在GitHub上找到原始程式碼。
身份驗證流程
以下步驟描述了身份驗證過程的一般流程。
- 您的客戶端使用
Connect()
將有關使用哪個身份驗證提供者的資訊和必要的身份驗證資料傳遞給Photon伺服器。這由Photon Bolt和Photon Cloud整合在內部處理。這由Photon Bolt和Photon Cloud整合在內部處理。 - Photon伺服器為您的應用程式獲取所需的身份驗證提供者,並採取以下步驟之一
- 找到身份驗證提供者配置->身份驗證繼續執行步驟3。
- 找不到身份驗證提供者配置->根據應用程式的設定,客戶端將被允許連接或拒絕
- Photon伺服器使用透過
Connect()
傳遞的身份驗證資訊調用身份驗證提供者。- 身份驗證提供者在線上->身份驗證繼續執行步驟4。
- 身份驗證提供者處於離線狀態->根據相應的提供者設定,客戶端將被允許連接或拒絕。
- 身份驗證提供者處理驗證資訊並將結果傳回給Photon伺服器
- 根據身份驗證結果,客戶端將被成功驗證或拒絕
Photon Cloud的設定
您可以從Photon應用程式的儀表板設定所有身份驗證提供者。
前往應用程式的詳細資訊頁面,打開自訂身份驗證部分。
新增身份驗證提供者
設置自訂身份驗證提供者很容易,可以在幾秒鐘內從您的Photon應用程式的儀表板完成。
如螢幕截圖所示,您可以輸入身份驗證URL,並如果您的身份驗證服務不在線上或因任何其他原因無法工作,則可以決定是否應拒絕客戶端。
此外,您可以選擇增加鍵/值對,這些對將作為査詢字串參數,隨每個請求一起發送到身份驗證服務。
- API公開金鑰,以確保請求確實來自Photon伺服器之一。
- API版本的自訂身份驗證,以更好地處理未來的更改。
此外,無論是否設置了任何身份驗證提供者,都有可能允許或拒絕嘗試連接到您的應用程式的匿名客戶端。
預設下,這是啟用的,這意味著最初所有客戶端都有權連接到您的應用程式,無論是否經過身份驗證。
您可以在Photon應用程式的儀表板的應用程式詳細資訊頁面上的身份驗證部分查看此資訊。
當您增加至少一個身份驗證提供者時,才會顯示該選項。
如果沒有設置身份驗證提供者,它將以預設值(啟用)隱藏。
更新或刪除身份驗證提供者
此外,在應用程式詳細資訊頁面中,您可以選擇編輯現有的身份驗證提供者。
在編輯表單上,您可以更新所有設定或完全刪除身份驗證提供者。
實作
客戶端側
在客戶端側,API將處理自訂身份驗證-只需設定一次相關參數和目標自訂身份驗證服務。
設定後,連接並處理最終錯誤。
示例:
為了將您的玩家的身份驗證憑證傳遞到Photon Bolt迴圈中,只需初始化PhotonPlatform
並傳遞一個Photon.Realtime.AuthenticationValues
的執行個體即可。
Bolt將負責將此資訊發送到Photon Cloud,並處理來自您的Auth伺服器的任何資料。
下方是一個範例程式碼,顯示了如何創建新的PhotonPlatform
並傳遞AuthenticationValues
。
在啟動同儕節點之前,您 必須 設定UdpPlatform
,無論是作為伺服器還是客戶端。
C#
using Photon.Realtime;
using UdpKit.Platform;
public class Menu : Bolt.GlobalEventListener
{
public void StartServer(string user, string password)
{
SetupPlatform(user, password);
BoltLauncher.StartServer();
}
public void StartClient(string user, string password)
{
SetupPlatform(user, password);
BoltLauncher.StartClient();
}
private void SetupPlatform(string user, string password)
{
var auth = new AuthenticationValues();
auth.AuthType = CustomAuthenticationType.Custom;
auth.AddAuthParameter("user", user);
auth.AddAuthParameter("pass", password);
auth.UserId = "my local user ID";
var platform = new PhotonPlatform(new PhotonPlatformConfig()
{
AuthenticationValues = auth
});
BoltLauncher.SetUdpPlatform(platform);
}
}
在這個程式碼片段中,為了簡化事情,我們選擇了一個非常基本的基於密碼的身份驗證憑證。
這將產生以下査詢字串:?user={user}&pass={pass}
。
通常,這些憑證是一對值,第一個值是唯一識別字(用戶ID、用戶名、電子郵件等),另一個是「真實性證明」(雜湊密碼、金鑰、秘密、令牌等)。
出於安全原因,不建議發送純文字密碼。
身份驗證操作
身份驗證操作是將身份驗證值實際發送到伺服器的地方。
它通常由我們的API使用,而不是直接由您的客戶端程式碼使用。
需要注意的是:連接到伺服器總是包括一個身份驗證步驟。
第一次,操作將實際身份驗證值作為加密操作發送。
對於後續的伺服器交換,Photon提供了自己的令牌,該令牌經過加密並自動使用。
伺服器側
一旦web伺服器收到身份驗證請求,就應該檢查和驗證査詢參數。
例如,可以將憑證與存儲在資料庫中的現有憑證進行比較。
如果接收到的參數缺失或無效,則返回的結果應為{ "ResultCode": 3, "Message": "Invalid parameters." }
完成驗證後,應按如下方式返回結果:
- 成功:
{ "ResultCode": 1, "UserId": <userId> }
- 失敗:
{ "ResultCode": 2, "Message": "Authentication failed. Wrong credentials." }
高級功能
除了對用戶進行身份驗證外,還可以從身份驗證提供者返回額外的資訊。
為了做到這一點,用戶應該在客戶端和扮演「身份驗證器」角色的web服務之間建立某種協定。
向伺服器發送資料
最簡單的方法是「要麼全有要麼全無」的策略:
選擇是否向客戶端返回靜態數量的變數。
但有些用例需要一種更複雜的方法,即web服務根據客戶端的請求「按需」返回資料。
本小節解釋了客戶端如何向web服務發送資料。
資料可以是身份驗證所需的憑證,也可以加上任何額外的參數。
除其他事項外,額外的參數可用於請求在身份驗證回應中返回伺服器端的可用的資料。
這非常有用,因為它可以節省額外的API調用並簡化登入工作流程。
在極少數情況下,身份驗證可能需要大量資料。
另一方面,大多數web伺服器對査詢字串中使用的字元數或URL長度有限制。
這就是為什麼Photon提供了從客戶端將HTTP方法更改為POST的可能性,在C# SDK中,這是通過顯式設定AuthenticationValues.AuthPostData
欄位為一個值來完成的。
後者的類型可以是string
、byte[]
或Dictionary<string, object>
。
在Dictionary<string, object>
的情況下,載荷將被轉換為JSON字串,而HTTP請求的內容-類型將被設定為「applicaton/json」。
在C# SDK中,AuthenticationValues
類別為每種支援的類型提供了setter方法。
由於這可能是一個要求或約束,因此POST方法選項也適用於選擇以POST方法從web服務接收身份驗證請求的任何人。
換句話說,要發送身份驗證參數,您可以自由使用査詢字串或POST資料,或兩者兼而有之。
下表給出了可能的組合。
AuthPostData | AuthGetParameters | HTTP方法 |
---|---|---|
空值 | * | GET |
空的字串 | * | GET |
字串(不是空值,不是空的) | * | POST |
位元[](不是空值,可以是空的) | * | POST |
字典<字串,物件>(不是空值,可以是空的) | * | POST (內容-類型="application/json") |
將資料返回給客戶端
由於Photon伺服器是客戶端和web服務之間的代理,因此您應該注意Photon伺服器可以處理的變數。
與Photon伺服器收到的所有HTTP傳入回應一樣,web伺服器應返回一個JSON物件,其中包括一個ResultCode
和一個可選的Message
。
此外,這裡列出了Photon伺服器在身份驗證期間可以從web服務中獲得什麼。
UserId
:
這可以用作身份驗證本身的參數,也可以從客戶端請求。
當Photon伺服器接收到該消息時,它始終會轉發給客戶端。
否則,如果AuthenticationValues.UserId
最初沒有設定,隨機生成的UserId將被發送回客戶端。
這將覆寫客戶端中的UserId
值,且此後無法更改。
僅當ResultCode
值為1時,才應返回此值。
示例:{ "ResultCode": 1, "UserId": "SomeUniqueStringId" }
Data
:JSON物件,包含應返回給客戶端的任何額外值。
請記住,不支援巢狀陣列或物件。
僅當ResultCode
值為0或1時,才應返回它。
示例:{ "ResultCode": 0, "Data": { "S": "Vpqmazljnbr=", "A": [ 1, -5, 9 ] } }
ResultCode
是唯一必需的返回變數,其他任何變數都是可選的。
下表總結了web伺服器可能返回的內容。
ResultCode | 說明 | UserId | 暱稱 | AuthCookie | 資料 |
---|---|---|---|---|---|
0 | 身份驗證未完成,只傳回資料。* | ||||
1 | 身份驗證成功。 | (可選) | (可選) | (可選) | (可選) |
2 | 身份驗證失敗。錯誤憑證。 | ||||
3 | 無效參數。 |
*:例如這可能有助於實作OAuth 2.0或兩步驗證。
從客戶端讀取資料
啟動Photon Bolt後,它將自動連接到Photon Cloud,運行前面描述的身份驗證過程。
完成此過程後,將叫用BoltStartDone
,此時,您將能夠透過檢查BoltMatchmaking.CurrentMetadata
屬性,來讀取您的伺服器發送的所有自訂資訊。
這由Photon整合填入,使其可供使用。
以下是如何從回應中獲取返回值的程式碼片段:
C#
using System.Collections.Generic;
using Bolt.Matchmaking;
using Bolt.Utils;
using Photon.Realtime;
using UdpKit.Platform;
public class Menu : Bolt.GlobalEventListener
{
// ...
public override void BoltStartDone()
{
var meta = BoltMatchmaking.CurrentMetadata;
// Read all custom data sent from your auth server
Dictionary<string, object> customData;
if (meta.TryGetValue("Data", out customData))
{
var text = string.Empty;
foreach (var item in customData)
{
text += string.Format("{0} : {1}\n", item.Key, item.Value);
}
BoltLog.Info(text);
}
// Read the UserId of the local player
string userID;
if (meta.TryGetValue("UserId", out userID))
{
BoltLog.Info("UserID: {0}", userID);
}
// Read the Nickname of the local player
string nickName;
if (meta.TryGetValue("Nickname", out nickName))
{
BoltLog.Info("Nickname: {0}", nickName);
}
// Your usual BoltStartDone behaviour: setup game server or join a session
}
}
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"
]
}
故障排除
當自訂身份驗證失敗時,會觸發BoltStartFailed
回調,並附上UdpConnectionDisconnectReason.Authentication
作為引數值。
如前所述,當您嘗試啟動Bolt時,會發生身份驗證過程。
如果您的伺服器使您的玩家的身份驗證憑證無效,Bolt將無法啟動並透過以下方法報告此情況:
C#
public override void BoltStartFailed(UdpConnectionDisconnectReason disconnectReason)
{
Debug.LogErrorFormat("BoltStartFailed. Reason: {0}", disconnectReason);
}
如果您在儀表板中設置的身份驗證URL返回一些HTTP錯誤,Photon伺服器會暫停身份驗證調用一段時間,以避免一些開銷。
在設置或測試URL時,請考慮此「回退」時間。
最佳做法
- 身份驗證提供者返回的結果應包含可讀的
Message
,特別是在失敗的情況下。
這將為您節省大量偵錯的麻煩。 - 在儀表板上,設定不應從客戶端側設定的靜態鍵/值對。
這將防止生成的査詢字串中出現重複鍵。 - 出於安全原因,不要將純文字密碼作為身份驗證參數發送。
- 建議從Photon儀表板設定査詢字串參數。
這樣您就可以檢查請求的來源。 - 使用
AuthenticationValues
方法設定參數,不要直接影響AuthGetParameters
的值。
這將防止査詢字串格式錯誤。
用例示例:封鎖舊客戶端版本
您可以使用自訂身份驗證來拒絕來自使用舊版本(或意外版本)的客戶端的連接,並返回特定錯誤,以便您可以要求用戶進行更新。
為此,您需要在自訂身份驗證請求中發送版本。由您決定是將其作為査詢字串參數還是POST資料引數來完成它。
在下方的示例中,我們將使用査詢字串參數:
C#
private void SetupPlatform(string user, string password)
{
var version = BoltNetwork.CurrentVersion;
var auth = new AuthenticationValues();
auth.AuthType = CustomAuthenticationType.Custom;
auth.AddAuthParameter("version", version);
var platform = new PhotonPlatform(new PhotonPlatformConfig()
{
AuthenticationValues = auth
});
BoltLauncher.SetUdpPlatform(platform);
}
如果您的自訂身份驗證URL為https://example.com
,則請求將按此方式發送https://example.com?version={version}
。
從您的身份驗證提供者實作中,您應該獲取並比較收到的版本。
如果版本是允許的,則返回{ "ResultCode": 1 }
。
如果沒有,您應該返回一個帶有您選擇的自訂值(不同於1)的ResultCode
,最好是帶一條訊息。
示例:{ "ResultCode": 5, "Message": "Version not allowed." }
。