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 应用程序中定义了名为“管理员”的角色,则可以指定只有该角色中的用户才能使用以下代码访问中心。
[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 属性应用于中心或方法,以指定其他要求。 在属性中指定的任何要求将添加到身份验证的基本要求中。
以下示例演示了一个启动文件,该文件将所有中心方法限制为经过身份验证的用户。
public partial class Startup {
public void Configuration(IAppBuilder app) {
app.MapSignalR();
GlobalHost.HubPipeline.RequireAuthentication();
}
}
如果在处理 SignalR 请求后调用 RequireAuthentication()
方法,SignalR 将引发 InvalidOperationException
异常。 SignalR 引发此异常,因为在调用管道后无法将模块添加到 HubPipeline。 前面的示例演示了在处理第 RequireAuthentication
一个请求之前执行一次的方法中 Configuration
调用 方法。
自定义授权
如果需要自定义授权的确定方式,可以创建派生自 AuthorizeAttribute
的类,并重写 UserAuthorized 方法。 对于每个请求,SignalR 都会调用此方法来确定用户是否有权完成请求。 在重写的方法中,为授权方案提供必要的逻辑。 以下示例演示如何通过基于声明的标识强制执行授权。
[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 客户端的详细信息,请参阅 中心 API 指南 - .NET 客户端。
Cookie
当 .NET 客户端与使用 ASP.NET 窗体身份验证的中心交互时,需要在连接上手动设置身份验证 Cookie。 将 Cookie 添加到 CookieContainer
HubConnection 对象的 属性。 以下示例演示一个控制台应用,该应用从网页中检索身份验证 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();
}
}