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 に投稿できます。
概要
このトピックは、次のセクションで構成されています。
Authorize 属性
SignalR には、ハブまたはメソッドにアクセスできるユーザーまたはロールを指定するための Authorize 属性が用意されています。 この属性は Microsoft.AspNet.SignalR
名前空間にあります。 Authorize
属性は、ハブまたはハブ内の特定のメソッドに適用します。 Authorize
属性をハブ クラスに適用すると、指定された認可要件がハブ内のすべてのメソッドに適用されます。 このトピックでは、適用できるさまざまな種類の認可要件の例を示します。 Authorize
属性がない場合、接続されたクライアントはハブ上の任意のパブリック メソッドにアクセスできます。
Web アプリケーションで "Admin" というロールを定義した場合は、次のコードを使って、そのロールのユーザーのみがハブにアクセスできるように指定できます。
[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 にモジュールを追加できないためです。 前の例は、最初の要求を処理する前に 1 回実行される Configuration
メソッド内の RequireAuthentication
メソッドの呼び出しを示しています。
カスタマイズされた認可
認可の決定方法をカスタマイズする必要がある場合は、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
};
}
}
あるクライアントの接続 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();
}
}
次に、ハブでユーザーのトークンを確認します。
証明書
クライアント証明書を渡してユーザーを確認できます。 接続を作成するときに証明書を追加します。 次の例は、クライアント証明書を接続に追加する方法のみを示しています。コンソール アプリ全体は表示されません。 これは、証明書を作成する方法をいくつか提供する 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();
}
}