共用方式為


SignalR 中樞的驗證與授權 (SignalR 1.x)

作者 :Patrick FletcherTom 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 用戶端

當您的 .NET 用戶端與使用 ASP.NET Forms Authentication 的中樞互動時,您必須在連線上手動設定驗證 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();
    }
}