作者:Patrick Fletcher、Tom FitzMacken
警告
本檔不適用於最新版本的 SignalR。 看看 ASP.NET Core SignalR。
本主題描述如何限制哪些使用者或角色可以存取中樞方法。
概觀
本主題包含下列幾節:
Authorize 屬性
SignalR 提供 Authorize 屬性,以指定哪些使用者或角色可以存取中樞或方法。 這個屬性位於 命名空間中 Microsoft.AspNet.SignalR 。 您可以將 Authorize 屬性套用到中樞或中樞中的特定方法。 當您將 Authorize 屬性套用至中樞類別時,指定的授權需求會套用至中樞中的所有方法。 您可以套用的不同授權需求類型如下所示。
Authorize如果沒有 屬性,中樞上的所有公用方法都可供連線到中樞的用戶端使用。
如果您已在 Web 應用程式中定義名為 「Admin」 的角色,您可以指定只有該角色中的使用者可以使用下列程式代碼來存取中樞。
[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 屬性套用至中樞或方法,以指定其他需求。 除了驗證的基本需求之外,您在屬性中指定的任何需求也會套用。
下列範例顯示 Global.asax 檔案,其會將所有中樞方法限製為已驗證的使用者。
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
GlobalHost.HubPipeline.RequireAuthentication();
}
}
如果您在處理 SignalR 要求之後呼叫 RequireAuthentication() 方法,SignalR 將會擲回 InvalidOperationException 例外狀況。 這個例外狀況會被拋出,因為在管線被叫用後,您無法將模組新增至 HubPipeline。 前一個範例顯示了在方法 RequireAuthentication 中呼叫 Application_Start 方法,該方法會在處理第一個要求之前執行一次。
自訂授權
如果您需要自定義授權的判斷方式,您可以建立衍生自 AuthorizeAttribute 的類別,並覆寫 UserAuthorized 方法。 系統會針對每個要求呼叫這個方法,以判斷使用者是否獲得完成要求的授權。 在覆寫的方法中,您會提供授權情境的必要邏輯。 下列範例示範如何透過宣告型身分識別強制執行授權。
[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 = (ClaimsPrincipal)user;
if (principal != null)
{
Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
return authenticated.Value == "true" ? true : 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 應用程式。 如需有關使用 SignalR 的 .NET 用戶端的詳細資訊,請參閱 Hubs API 指南 - .NET 用戶端。
餅乾
當您的 .NET 用戶端與使用 ASP.NET Forms Authentication 的 Hub 互動時,您必須在連線上手動設定驗證的 Cookie。 您將 Cookie 新增至 CookieContainerHubConnection 物件的屬性上。 下列範例顯示主控台應用程式,從網頁擷取驗證 Cookie,並將該 Cookie 新增至連線。 範例中的 URL https://www.contoso.com/RemoteLogin 會指向您需要建立的網頁。 此頁面會擷取張貼的使用者名稱和密碼,並嘗試使用認證登入使用者。
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();
}
}