SignalR ハブの認証と承認

作成者: Patrick FletcherTom FitzMacken

警告

このドキュメントは、SignalR の最新バージョン用ではありません。 SignalR の ASP.NET Coreを見てみましょう。

このトピックでは、ハブ メソッドにアクセスできるユーザーまたはロールを制限する方法について説明します。

このトピックで使用するソフトウェアのバージョン

このトピックの以前のバージョン

SignalR の以前のバージョンの詳細については、「 SignalR の古いバージョン」を参照してください。

質問とコメント

このチュートリアルが気に入った方法と、ページの下部にあるコメントで改善できる内容に関するフィードバックをお寄せください。 チュートリアルに直接関連しない質問がある場合は、 ASP.NET SignalR フォーラム または StackOverflow.com に投稿できます。

概要

このトピックは、次のセクションで構成されています。

Authorize 属性

SignalR には、ハブまたはメソッドにアクセスできるユーザーまたはロールを指定する Authorize 属性が用意されています。 この属性は 名前空間にあります Microsoft.AspNet.SignalR 。 ハブまたはハブ内の Authorize 特定のメソッドに属性を適用します。 ハブ クラスに属性を Authorize 適用すると、指定した承認要件がハブ内のすべてのメソッドに適用されます。 このトピックでは、適用できるさまざまな種類の承認要件の例を示します。 属性がない Authorize 場合、接続されたクライアントはハブ上の任意のパブリック メソッドにアクセスできます。

Web アプリケーションで "管理" という名前のロールを定義している場合は、そのロールのユーザーのみが次のコードを使用してハブにアクセスできるように指定できます。

[Authorize(Roles = "Admin")] 
public class AdminAuthHub : Hub 
{ 
}

または、次に示すように、ハブに、すべてのユーザーが使用できる 1 つのメソッドと、認証されたユーザーのみが使用できる 2 つ目のメソッドが含まれていることを指定できます。

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 に追加できないため、この例外をスローします。 前の例は、最初の要求を RequireAuthentication 処理する前に Configuration 1 回実行される メソッドで メソッドを呼び出す方法を示しています。

カスタマイズされた承認

承認の決定方法をカスタマイズする必要がある場合は、 から AuthorizeAttribute 派生するクラスを作成し、 UserAuthorized メソッドをオーバーライドできます。 SignalR は、要求ごとにこのメソッドを呼び出して、ユーザーが要求を完了する権限があるかどうかを判断します。 オーバーライドされたメソッドでは、承認シナリオに必要なロジックを指定します。 次の例は、要求ベースの ID を使用して承認を適用する方法を示しています。

[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
        };
    }
}

悪意のあるユーザーがそのクライアントからの要求を模倣するために使用する可能性があるため、1 つのクライアントの接続 ID を他のクライアントに渡さないでください。

.NET クライアントの認証オプション

認証されたユーザーに限定されたハブと対話するコンソール アプリなどの .NET クライアントがある場合は、Cookie、接続ヘッダー、または証明書で認証資格情報を渡すことができます。 このセクションの例では、これらのさまざまな方法を使用してユーザーを認証する方法を示します。 完全に機能する SignalR アプリではありません。 SignalR を使用した .NET クライアントの詳細については、「 Hubs API ガイド - .NET クライアント」を参照してください。

.NET クライアントが ASP.NET フォーム認証を使用するハブと対話する場合は、接続で認証 Cookie を手動で設定する必要があります。 Cookie を HubConnection オブジェクトの CookieContainer プロパティに追加します。 次の例は、Web ページから認証 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();
    }
}

次に、ハブで、ユーザーのトークンを確認します。

Certificate

クライアント証明書を渡して、ユーザーを確認できます。 接続の作成時に証明書を追加します。 次の例は、接続にクライアント証明書を追加する方法のみを示しています。完全なコンソール アプリは表示されません。 証明書を作成するいくつかの異なる方法を提供する 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();
    }
}