Assign/Push SignalR 上下文的 ClaimsPrincipal
Assign/Push ClaimsPrincipal to SignalR Context
好吧,所以这肯定是把我推向了悬崖的边缘......在相对轻松地使用 SignalR
的 V1 后,我现在肯定在挣扎。
问题: "Grabbing" Claims
来自最近通过身份验证的用户和 passing/using 在 Hub
环境:
WebAPI 在 SSL
上使用 Basic Auth
进行身份验证。验证逻辑在一个属性中并且工作正常并且已经持续了几个月,它还在 AuthenticateAsync
中声明 generates/builds
在我的 API Controller
中,我抓住了声明:
var idenityInstance = new IdentityInstance(this.User as ClaimsPrincipal);
我整个周末都在尝试利用 Groups/SSL 和身份验证合并 SignalR 的 V2。
我通常认为 ClaimsPrincipal 会被忽略或在相同的 "Context" 中被忽略。
public void Configuration(IAppBuilder app)
{
// Branch the pipeline here for requests that start with "/signalr"
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
自定义授权属性(从文档中获取)
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;
}
}
好的,最后一个片段,The Hub(剥离)
public async Task Hello()
{
//Temp line for testing under local SSL/Self signed
ServicePointManager.ServerCertificateValidationCallback
+= (sender, cert, chain, sslPolicyErrors) => true;
var hubConnection = new HubConnection("https://localhost:44357");
IHubProxy chatHubProxy = hubConnection.CreateHubProxy("printerHub");
hubConnection.Start().Wait();
var c = Context;
await hubContext.Clients.All.hello("Hello");
}
同样的问题是,当我通过 Hub Method Hello();
进行调试时,Context
始终是 null
,最终不会 authenticate
,因为自定义 attribute
我希望我刚刚错过了一些愚蠢的东西,因为它确实非常令人沮丧。
好的,这比最初预期的要棘手一些,现在解决方案可能不适合 "some" 环境,但在这种情况下,解决了问题。
始终牢记我们正在处理两个 Contexts
...
重述一下,我有一个 WebAPI
和 basic auth
超过 SSL
- 它工作得非常好,现在的想法是提供一种方法来锁定 Hubs
并且只允许访问 已验证 用户和 basic auth
范围之外的用户。由于 WebAPI
和 SignalR
运行 中两个进程对 Hubs
的访问可以在 WebAPI
管道之外。
一点代码来演示:
枢纽:
[HubAuthorizationAttribute]
[HubName("printerHub")]
public class PrinterHub : Hub
{
private static IHubContext hubContext =
GlobalHost.ConnectionManager.GetHubContext<PrinterHub>();
private IdentityInstance _userInstanceContext;
private User _user;
public override Task OnConnected()
{
//Custom logic to return a User object
_userInstanceContext = new IdentityInstance(this.Context.User as ClaimsPrincipal);
_user = _userInstanceContext.Create();
return base.OnConnected();
}
public override Task OnDisconnected(bool stopping)
{
return base.OnDisconnected(stopping);
}
public async Task JoinPrinterNotifications()
{
await hubContext.Groups.Add(Context.ConnectionId, _user.ClientName + ":Printers");
}
public async Task PrintQueues(IEnumerable<QueuedPrintItem> items)
{
await hubContext.Clients.Group(_user.ClientName + ":Printers").notify(items);
}
SignalR 自定义身份验证属性
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor,
IRequest request)
{
//This should be sent in the Header - but will not work with
//Websockets, if a custom header is required, ensure the transport type is "LongPolling" for demo purposes, QueryString was chosen.
var token = request.QueryString.Get("Token");
if (string.IsNullOrWhiteSpace(token))
return false;
//Logic omitted to perform checks and the validity of a request
if (client == null)
return false;
//build the claims identity
Claim clientIdClaim = new Claim("Id", "SignalR: " + client.Id.ToString());
Claim clientNameClaim = new Claim(ClaimTypes.Name, "SignalR: " + client.ClientName);
List<Claim> claims = new List<Claim>
{
clientIdClaim,
clientNameClaim,
};
//"Basic" is required to ensure IsAuthenticated is set, amongst others.
ClaimsIdentity identity = new ClaimsIdentity(claims, "Basic");
// set the authenticated user principal into environment so that it can be used in the future
request.Environment["server.User"] = new ClaimsPrincipal(identity);
return true;
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
var connectionId = hubIncomingInvokerContext.Hub.Context.ConnectionId;
// check the authenticated user principal from environment
var environment = hubIncomingInvokerContext.Hub.Context.Request.Environment;
var principal = environment["server.User"] as ClaimsPrincipal;
if (principal == null ||
principal.Identity == null ||
principal.Identity.IsAuthenticated == false)
return false;
// create a new HubCallerContext instance with the principal generated from token
// and replace the current context so that in hubs we can retrieve current user identity
hubIncomingInvokerContext.Hub.Context = new HubCallerContext(new ServerRequest(environment), connectionId);
return true;
}
好吧,所以这肯定是把我推向了悬崖的边缘......在相对轻松地使用 SignalR
的 V1 后,我现在肯定在挣扎。
问题: "Grabbing" Claims
来自最近通过身份验证的用户和 passing/using 在 Hub
环境:
WebAPI 在 SSL
上使用 Basic Auth
进行身份验证。验证逻辑在一个属性中并且工作正常并且已经持续了几个月,它还在 AuthenticateAsync
在我的 API Controller
中,我抓住了声明:
var idenityInstance = new IdentityInstance(this.User as ClaimsPrincipal);
我整个周末都在尝试利用 Groups/SSL 和身份验证合并 SignalR 的 V2。
我通常认为 ClaimsPrincipal 会被忽略或在相同的 "Context" 中被忽略。
public void Configuration(IAppBuilder app)
{
// Branch the pipeline here for requests that start with "/signalr"
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
自定义授权属性(从文档中获取)
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;
}
}
好的,最后一个片段,The Hub(剥离)
public async Task Hello()
{
//Temp line for testing under local SSL/Self signed
ServicePointManager.ServerCertificateValidationCallback
+= (sender, cert, chain, sslPolicyErrors) => true;
var hubConnection = new HubConnection("https://localhost:44357");
IHubProxy chatHubProxy = hubConnection.CreateHubProxy("printerHub");
hubConnection.Start().Wait();
var c = Context;
await hubContext.Clients.All.hello("Hello");
}
同样的问题是,当我通过 Hub Method Hello();
进行调试时,Context
始终是 null
,最终不会 authenticate
,因为自定义 attribute
我希望我刚刚错过了一些愚蠢的东西,因为它确实非常令人沮丧。
好的,这比最初预期的要棘手一些,现在解决方案可能不适合 "some" 环境,但在这种情况下,解决了问题。
始终牢记我们正在处理两个 Contexts
...
重述一下,我有一个 WebAPI
和 basic auth
超过 SSL
- 它工作得非常好,现在的想法是提供一种方法来锁定 Hubs
并且只允许访问 已验证 用户和 basic auth
范围之外的用户。由于 WebAPI
和 SignalR
运行 中两个进程对 Hubs
的访问可以在 WebAPI
管道之外。
一点代码来演示:
枢纽:
[HubAuthorizationAttribute]
[HubName("printerHub")]
public class PrinterHub : Hub
{
private static IHubContext hubContext =
GlobalHost.ConnectionManager.GetHubContext<PrinterHub>();
private IdentityInstance _userInstanceContext;
private User _user;
public override Task OnConnected()
{
//Custom logic to return a User object
_userInstanceContext = new IdentityInstance(this.Context.User as ClaimsPrincipal);
_user = _userInstanceContext.Create();
return base.OnConnected();
}
public override Task OnDisconnected(bool stopping)
{
return base.OnDisconnected(stopping);
}
public async Task JoinPrinterNotifications()
{
await hubContext.Groups.Add(Context.ConnectionId, _user.ClientName + ":Printers");
}
public async Task PrintQueues(IEnumerable<QueuedPrintItem> items)
{
await hubContext.Clients.Group(_user.ClientName + ":Printers").notify(items);
}
SignalR 自定义身份验证属性
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor,
IRequest request)
{
//This should be sent in the Header - but will not work with
//Websockets, if a custom header is required, ensure the transport type is "LongPolling" for demo purposes, QueryString was chosen.
var token = request.QueryString.Get("Token");
if (string.IsNullOrWhiteSpace(token))
return false;
//Logic omitted to perform checks and the validity of a request
if (client == null)
return false;
//build the claims identity
Claim clientIdClaim = new Claim("Id", "SignalR: " + client.Id.ToString());
Claim clientNameClaim = new Claim(ClaimTypes.Name, "SignalR: " + client.ClientName);
List<Claim> claims = new List<Claim>
{
clientIdClaim,
clientNameClaim,
};
//"Basic" is required to ensure IsAuthenticated is set, amongst others.
ClaimsIdentity identity = new ClaimsIdentity(claims, "Basic");
// set the authenticated user principal into environment so that it can be used in the future
request.Environment["server.User"] = new ClaimsPrincipal(identity);
return true;
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
var connectionId = hubIncomingInvokerContext.Hub.Context.ConnectionId;
// check the authenticated user principal from environment
var environment = hubIncomingInvokerContext.Hub.Context.Request.Environment;
var principal = environment["server.User"] as ClaimsPrincipal;
if (principal == null ||
principal.Identity == null ||
principal.Identity.IsAuthenticated == false)
return false;
// create a new HubCallerContext instance with the principal generated from token
// and replace the current context so that in hubs we can retrieve current user identity
hubIncomingInvokerContext.Hub.Context = new HubCallerContext(new ServerRequest(environment), connectionId);
return true;
}