SignalR 中樞的驗證和授權
作者: Patrick Fletcher、 Tom FitzMacken
警告
本檔不適用於最新版的 SignalR。 請查看ASP.NET Core SignalR。
本主題描述如何限制哪些使用者或角色可以存取中樞方法。
本主題中使用的軟體版本
- Visual Studio 2013
- .NET 4.5
- SignalR 第 2 版
本主題的舊版
如需舊版 SignalR 的相關資訊,請參閱 SignalR 舊版。
問題和批註
請留下您喜歡本教學課程的意見反應,以及我們可以在頁面底部的批註中改善的內容。 如果您有與教學課程不直接相關的問題,您可以將問題張貼到 ASP.NET SignalR 論壇 或 StackOverflow.com。
概觀
本主題包含下列幾節:
授權屬性
SignalR 提供 Authorize 屬性,以指定哪些使用者或角色可以存取中樞或方法。 此屬性位於 命名空間中 Microsoft.AspNet.SignalR
。 您可以將 屬性套用 Authorize
至中樞或中樞中的特定方法。 當您將 屬性套用至中 Authorize
樞類別時,指定的授權需求會套用至中樞中的所有方法。 本主題提供您可以套用的不同授權需求類型範例。 Authorize
如果沒有 屬性,連線的用戶端就可以存取中樞上的任何公用方法。
如果您已在 Web 應用程式中定義名為 「管理員」 的角色,您可以指定只有該角色中的使用者可以使用下列程式碼存取中樞。
[Authorize(Roles = "Admin")]
public class AdminAuthHub : Hub
{
}
或者,您可以指定中樞包含一個可供所有使用者使用的方法,以及只能供已驗證的使用者使用的第二個方法,如下所示。
public class SampleHub : Hub
{
public void UnrestrictedSend(string message){ . . . }
[Authorize]
public void AuthenticatedSend(string message){ . . . }
}
下列範例可解決不同的授權案例:
[Authorize]
– 僅限已驗證的使用者[Authorize(Roles = "Admin,Manager")]
– 只有指定角色中已驗證的使用者[Authorize(Users = "user1,user2")]
– 只有具有指定使用者名稱的已驗證使用者[Authorize(RequireOutgoing=false)]
– 只有已驗證的使用者可以叫用中樞,但從伺服器回到用戶端的呼叫不受授許可權制,例如,只有特定使用者可以傳送訊息,但所有其他使用者都可以接收訊息時。 RequireOutgoing 屬性只能套用至整個中樞,而不能套用至中樞內的個別方法。 當 RequireOutgoing 未設定為 false 時,只會從伺服器呼叫符合授權需求的使用者。
需要所有中樞的驗證
您可以在應用程式啟動時呼叫 RequireAuthentication 方法,以要求驗證應用程式中的所有中樞和中樞方法。 當您有多個中樞,而且想要對所有中樞強制執行驗證需求時,可能會使用此方法。 使用此方法時,您無法指定角色、使用者或傳出授權的需求。 您只能指定對中樞方法的存取僅限於已驗證的使用者。 不過,您仍然可以將 Authorize 屬性套用至中樞或方法,以指定其他需求。 您在屬性中指定的任何需求會新增至驗證的基本需求。
下列範例顯示啟動檔案,其會將所有中樞方法限制為已驗證的使用者。
public partial class Startup {
public void Configuration(IAppBuilder app) {
app.MapSignalR();
GlobalHost.HubPipeline.RequireAuthentication();
}
}
如果您在處理 SignalR 要求之後呼叫 RequireAuthentication()
方法,SignalR 會擲回 InvalidOperationException
例外狀況。 SignalR 會擲回此例外狀況,因為您無法在叫用管線之後,將模組新增至 HubPipeline。 上一個範例示範在處理第一個要求之前一次執行的方法中 Configuration
呼叫 RequireAuthentication
方法。
自訂授權
如果您需要自訂授權的判斷方式,您可以建立衍生自 AuthorizeAttribute
的類別,並覆寫 UserAuthorized 方法。 針對每個要求,SignalR 會叫用這個方法來判斷使用者是否獲得授權來完成要求。 在覆寫的 方法中,您會提供授權案例的必要邏輯。 下列範例示範如何透過宣告型身分識別強制執行授權。
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class AuthorizeClaimsAttribute : AuthorizeAttribute
{
protected override bool UserAuthorized(System.Security.Principal.IPrincipal user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var principal = user as ClaimsPrincipal;
if (principal != null)
{
Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
if (authenticated != null && authenticated.Value == "true")
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
將驗證資訊傳遞至用戶端
您可能需要在用戶端上執行的程式碼中使用驗證資訊。 在用戶端上呼叫方法時,您會傳遞必要的資訊。 例如,聊天應用程式方法可以傳遞為張貼訊息的人員使用者名稱的參數,如下所示。
public Task SendChatMessage(string message)
{
string name;
var user = Context.User;
if (user.Identity.IsAuthenticated)
{
name = user.Identity.Name;
}
else
{
name = "anonymous";
}
return Clients.All.addMessageToPage(name, message);
}
或者,您可以建立 物件來表示驗證資訊,並將該物件當做參數傳遞,如下所示。
public class SampleHub : Hub
{
public override Task OnConnected()
{
return Clients.All.joined(GetAuthInfo());
}
protected object GetAuthInfo()
{
var user = Context.User;
return new
{
IsAuthenticated = user.Identity.IsAuthenticated,
IsAdmin = user.IsInRole("Admin"),
UserName = user.Identity.Name
};
}
}
您不應該將某個用戶端的連線識別碼傳遞給其他用戶端,因為惡意使用者可以使用它來模擬來自該用戶端的要求。
.NET 用戶端的驗證選項
當您有 .NET 用戶端,例如主控台應用程式,其與受限於已驗證使用者的中樞互動時,您可以在 Cookie、連線標頭或憑證中傳遞驗證認證。 本節中的範例示範如何使用這些不同的方法來驗證使用者。 它們不是功能完整的 SignalR 應用程式。 如需有關 .NET 用戶端與 SignalR 的詳細資訊,請參閱 中樞 API 指南 - .NET 用戶端。
Cookie
當您的 .NET 用戶端與使用 ASP.NET Forms Authentication 的中樞互動時,您必須在連線上手動設定驗證 Cookie。 您會將 Cookie 新增至 CookieContainer
HubConnection 物件上的 屬性。 下列範例顯示主控台應用程式,它會從網頁擷取驗證 Cookie,並將該 Cookie 新增至連線。
class Program
{
static void Main(string[] args)
{
var connection = new HubConnection("http://www.contoso.com/");
Cookie returnedCookie;
Console.Write("Enter user name: ");
string username = Console.ReadLine();
Console.Write("Enter password: ");
string password = Console.ReadLine();
var authResult = AuthenticateUser(username, password, out returnedCookie);
if (authResult)
{
connection.CookieContainer = new CookieContainer();
connection.CookieContainer.Add(returnedCookie);
Console.WriteLine("Welcome " + username);
}
else
{
Console.WriteLine("Login failed");
}
}
private static bool AuthenticateUser(string user, string password, out Cookie authCookie)
{
var request = WebRequest.Create("https://www.contoso.com/RemoteLogin") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.CookieContainer = new CookieContainer();
var authCredentials = "UserName=" + user + "&Password=" + password;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(authCredentials);
request.ContentLength = bytes.Length;
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(bytes, 0, bytes.Length);
}
using (var response = request.GetResponse() as HttpWebResponse)
{
authCookie = response.Cookies[FormsAuthentication.FormsCookieName];
}
if (authCookie != null)
{
return true;
}
else
{
return false;
}
}
}
主控台應用程式會將認證張貼至 www.contoso.com/RemoteLogin ,這可能參考包含下列程式碼後置檔案的空白頁面。
using System;
using System.Web.Security;
namespace SignalRWithConsoleChat
{
public partial class RemoteLogin : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string username = Request["UserName"];
string password = Request["Password"];
bool result = Membership.ValidateUser(username, password);
if (result)
{
FormsAuthentication.SetAuthCookie(username, false);
}
}
}
}
Windows 驗證
使用 Windows 驗證 時,您可以使用DefaultCredentials屬性傳遞目前使用者的認證。 您會將連線的認證設定為 DefaultCredentials 的值。
class Program
{
static void Main(string[] args)
{
var connection = new HubConnection("http://www.contoso.com/");
connection.Credentials = CredentialCache.DefaultCredentials;
connection.Start().Wait();
}
}
連接標頭
如果您的應用程式未使用 Cookie,您可以在連線標頭中傳遞使用者資訊。 例如,您可以在連接標頭中傳遞權杖。
class Program
{
static void Main(string[] args)
{
var connection = new HubConnection("http://www.contoso.com/");
connection.Headers.Add("myauthtoken", /* token data */);
connection.Start().Wait();
}
}
然後,在中樞中,您會驗證使用者的權杖。
憑證
您可以傳遞用戶端憑證來驗證使用者。 您可以在建立連線時新增憑證。 下列範例只會示範如何將用戶端憑證新增至連線;它不會顯示完整的主控台應用程式。 它會使用 X509Certificate 類別,提供數種不同的方法來建立憑證。
class Program
{
static void Main(string[] args)
{
var connection = new HubConnection("http://www.contoso.com/");
connection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
connection.Start().Wait();
}
}