Основная пользовательская аутентификация SignalR - Context.User.Identity является нулевым после того, как пользователь аутентифицирован в / согласовании - PullRequest
0 голосов
/ 08 марта 2019

Я написал пользовательскую аутентификацию для SignalR Core. Одной из функций является анонимный вход. Он создаст нового пользователя, если он подключится впервые. Код работает, но проблема в том, что аутентификация выполняется после того, как / myhub /gotiate очищен, а все утверждения в Context.User.Identity снова очищены, и IsAuthenticated изменяется на false, когда клиент запрос / myhub / . Только после этого претензии в Context.User.Identity не очищаются. Я попытался вернуть Fail, если это запрос на / myhub /gotiate , но тогда клиент не отправит запрос на / myhub / , если я это сделаю.

Есть идеи, как это исправить? Правильно ли реализован пользовательский вариант аутентификации?

Вот код всего класса, который я использую:

public class CustomAuthRequirementHandler : AuthorizationHandler<CustomAuthRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthRequirement requirement)
    {
        string name = context.User.Claims.Where(p => p.Type == ClaimTypes.NameIdentifier).Select(p => p.Value).SingleOrDefault();
        if (!context.User.Identity.IsAuthenticated)
            context.Fail();
        else
            context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

public class CustomAuthRequirement : IAuthorizationRequirement
{

}

public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
{
    public MyAuthenticationHandler(IOptionsMonitor<MyOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
    : base(options, logger, encoder, clock) { }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (Context.User.Identity != null && Context.User.Identity.IsAuthenticated) return await Task.FromResult(
                      AuthenticateResult.Success(
                         new AuthenticationTicket(
                             new ClaimsPrincipal(Options.Identity),
                             new AuthenticationProperties(),
                             this.Scheme.Name)));
        //if (Request.Path != "/myhub/") return await Task.FromResult(AuthenticateResult.Fail()); // only do authentication in /myhub/
        var u = CreateNewUser(); // connect to db create new user
        var claims = new List<Claim>() { };
        claims.Add(new Claim(ClaimTypes.Name, u.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, u.Id.ToString()));
        Options.Identity = new ClaimsIdentity(claims, "Custom");
        var user = new ClaimsPrincipal(Options.Identity);
        Context.User = user;
        return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, new AuthenticationProperties(), this.Scheme.Name)));                        
    }

}

public class MyOptions : AuthenticationSchemeOptions
{
    public ClaimsIdentity Identity { get; set; }

    public MyOptions()
    {

    }
}

Код конфигурации в ConfigureServices

        services.AddSingleton<IAuthorizationHandler, CustomAuthRequirementHandler>();
        services.AddAuthorization(p =>
        {
            p.AddPolicy("MainPolicy", builder =>
            {
                builder.Requirements.Add(new CustomAuthRequirement());
                builder.AuthenticationSchemes = new List<string> { "MyScheme" };
            });
        });

        services.AddAuthentication(o =>
        {
            o.DefaultScheme = "MyScheme";
        }).AddScheme<MyOptions, MyAuthenticationHandler>("MyScheme", "MyScheme", p => { });            
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        services.AddSignalR();

Настройка кода

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.UseAuthentication();
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseSignalR(routes =>
        {
            routes.MapHub<Hubs.MainHub>("/main");
        });
        app.UseMvc();
    }

Редактировать: добавлен код на стороне клиента

    @page
@{
    ViewData["Title"] = "Home page";
}

<input type="button" onclick="anonLogin()" value="AnonLogin" />
<script src="~/@@aspnet/signalr/dist/browser/signalr.js"></script>
<script type="text/javascript">
    var connection;    

    function anonLogin() {
        var token = "anon";
        connection = new signalR.HubConnectionBuilder().withUrl("/main?anon=" + token).build();        

        connection.start().then(function () {
            console.log("Connection ok");

            console.log("Sending message....");
            connection.invoke("Test").catch(function (err) {
                return console.error("Error sending message: " + err.toString());
            });
        }).catch(function (err) {
            console.log("Connection error: " + err.toString());
            return console.error(err.toString());
        });
    }
</script>

1 Ответ

0 голосов
/ 14 марта 2019

Я закончил тем, что создал поддельные заявки на идентификацию только для вызова / myhub /gotiate, так как этот вызов не важен, и ему просто необходима аутентификация, чтобы он мог перейти к /myhub/.

var u = new DomainUser() { Id = -1 };
        var claims = new List<Claim>() { };
        claims.Add(new Claim(ClaimTypes.Name, u.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.NameIdentifier, u.Id.ToString()));
        Options.Identity = new ClaimsIdentity(claims, "Custom");
        var user = new ClaimsPrincipal(Options.Identity);
        Context.User = user;
        return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), this.Scheme.Name)));
...