SignalR 中樞的驗證與授權 (SignalR 1.x)
作者 :Patrick Fletcher、 Tom FitzMacken
警告
本檔不適用於最新版的 SignalR。 看看ASP.NET Core SignalR。
本主題描述如何限制哪些使用者或角色可以存取中樞方法。
概觀
本主題包含下列幾節:
授權屬性
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 屬性套用至中樞或方法,以指定其他需求。 除了驗證的基本需求之外,還會套用您在屬性中指定的任何需求。
下列範例顯示 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 用戶端的詳細資訊,請參閱 中樞 API 指南 - .NET 用戶端。
Cookie
當您的 .NET 用戶端與使用 ASP.NET Forms Authentication 的中樞互動時,您必須在連線上手動設定驗證 Cookie。 您會將 Cookie 新增至 CookieContainer
HubConnection 物件上的 屬性。 下列範例顯示主控台應用程式,它會從網頁擷取驗證 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();
}
}