我似乎无法让SignalR核心使用cookie身份验证。我已经设置了一个测试项目,该项目可以成功进行身份验证并对需要授权的控制器进行后续调用。因此,常规身份验证似乎正在工作。
但之后,当我尝试连接到集线器,然后在标记为 Authorize
的集线器上触发方法时,调用将失败并显示以下消息:Authorization failed for user: (null)
我插入了一个虚拟中间件来检查请求的传入。从我的客户端(xamarin 移动应用(调用connection.StartAsync()
时,我收到一个OPTIONS
请求,context.User.Identity.IsAuthenticated
等于 true。紧接着我的集线器上的OnConnectedAsync
被调用。在这一点上_contextAccessor.HttpContext.User.Identity.IsAuthenticated
是错误的。什么负责取消对我的请求进行身份验证。从它离开我的中间件到调用 OnConnectedAsync 时,某些东西会删除身份验证。
有什么想法吗?
示例代码:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await this._next(context);
//At this point context.User.Identity.IsAuthenticated == true
}
}
public class TestHub: Hub
{
private readonly IHttpContextAccessor _contextAccessor;
public TestHub(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public override async Task OnConnectedAsync()
{
//At this point _contextAccessor.HttpContext.User.Identity.IsAuthenticated is false
await Task.FromResult(1);
}
public Task Send(string message)
{
return Clients.All.InvokeAsync("Send", message);
}
[Authorize]
public Task SendAuth(string message)
{
return Clients.All.InvokeAsync("SendAuth", message + " Authed");
}
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(options => options.UseInMemoryDatabase(databaseName: "MyDataBase1"));
services.AddIdentity<Auth, MyRole>().AddEntityFrameworkStores<MyContext>().AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options => {
options.Password.RequireDigit = false;
options.Password.RequiredLength = 3;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.User.RequireUniqueEmail = true;
});
services.AddSignalR();
services.AddTransient<TestHub>();
services.AddTransient<MyMiddleware>();
services.AddAuthentication();
services.AddAuthorization();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<MyMiddleware>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<TestHub>("TestHub");
});
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "{controller=App}/{action=Index}/{id?}");
});
}
}
这是客户端代码:
public async Task Test()
{
var cookieJar = new CookieContainer();
var handler = new HttpClientHandler
{
CookieContainer = cookieJar,
UseCookies = true,
UseDefaultCredentials = false
};
var client = new HttpClient(handler);
var json = JsonConvert.SerializeObject((new Auth { Name = "craig", Password = "12345" }));
var content = new StringContent(json, Encoding.UTF8, "application/json");
var result1 = await client.PostAsync("http://localhost:5000/api/My", content); //cookie created
var result2 = await client.PostAsync("http://localhost:5000/api/My/authtest", content); //cookie tested and works
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/TestHub")
.WithConsoleLogger()
.WithMessageHandler(handler)
.Build();
connection.On<string>("Send", data =>
{
Console.WriteLine($"Received: {data}");
});
connection.On<string>("SendAuth", data =>
{
Console.WriteLine($"Received: {data}");
});
await connection.StartAsync();
await connection.InvokeAsync("Send", "Hello"); //Succeeds, no auth required
await connection.InvokeAsync("SendAuth", "Hello NEEDSAUTH"); //Fails, auth required
}
如果您使用的是Core 2,请尝试更改UseAuthentication的顺序,将其放在UseSignalR方法之前。
app.UseAuthentication();
app.UseSignalR...
然后在中心内,标识属性不应为 null。
Context.User.Identity.Name
看起来这是WebSocketsTransport中的一个问题,我们不会将Cookie复制到websocket选项中。我们目前仅复制标头。我将提交一个问题来查看它。