Аутентификация SignalR Hub и возможность вызова внешних функций класса - PullRequest
0 голосов
/ 28 января 2020

У меня есть концентратор signalR, который не может вызвать функцию, которая получает мне роли моих пользователей из моей базы данных.

Вот мой стартап ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Contract.Extensions;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using SharedKernel;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Contract.API.Extensions;
using Contract.Data;
using Microsoft.EntityFrameworkCore;
using Contract.Factories;
using AutoMapper;
using Contract.API.Hubs;

namespace Contract.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            var connection = Configuration.GetConnectionString("ContractContext");

            services.AddAutoMapper();
            services.AddMvc();
            services.AddSignalR();
            services.AddContexts(connection);
            services.ConfigureSwaggerServices();
            services.RegisterServices();

            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
           .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

            var builder = EventExtensions.RegisterEvents();
            builder.Populate(services);
            var container = builder.Build();

            DomainEvents.Container = new NetEventContainer(container);
            return container.Resolve<IServiceProvider>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Db.ContractContext context)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            context.Database.Migrate();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseCors(builder =>
                            {
                                builder
                                    .WithOrigins("http://localhost:57704", "http://localhost:4200")
                                    .AllowAnyOrigin()
                                    .AllowAnyHeader()
                                    .AllowAnyMethod()
                                    .AllowCredentials();
                            });
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }



            app.ConfigureSwaggerApp();

            app.UseDefaultFiles();
            app.UseStaticFiles();
            app.UseMiddleware<SignalRMiddleware>();
            app.UseWebSockets();
            app.UseAuthentication();
            app.UseSignalR(routes =>
            {
                routes.MapHub<ContractHub>("/contractHub");
            });
            app.UseMvc();
        }
    }
}

Вот мой хаб ...

using Contract.API.ViewModels;
using Contract.Data.Query.User;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Contract.API.Hubs
{
    [Authorize]
    public class ContractHub : Hub
    {
        private readonly IUserInfoQuery _userInfoQuery;
        private static List<ActiveSignalRUser> activeUsers = new List<ActiveSignalRUser>();

        public ContractHub(IUserInfoQuery userInfoQuery)
        {
            _userInfoQuery = userInfoQuery;
        }

        public async Task AddToActiveUsers(ActiveSignalRUser user)
        {
            UserInfo appUser = _userInfoQuery.GetUserInfo();

            var sameContractUsers = new List<ActiveSignalRUser>();

            if (appUser.CanWrite == true)
            {
                var message = $"{user.UserName} has logged onto this contract. Please coordinate with them in order to prevent duplicate/overridden work flow";

                // alert Group that User has loggged on
                await Clients.Group(user.ContractNumber).SendAsync("notifyGroup", message);

                // add user to group named after contractNumber
                await Groups.AddToGroupAsync(Context.ConnectionId, user.ContractNumber);

                // add user to active global users
                user.ConnectionId = Context.ConnectionId;
                activeUsers.Add(user);

                var count = activeUsers.Where(a => a.ContractNumber == user.ContractNumber).Count();
                if (count > 1 && count < 3)
                {
                    // send message about previous users
                    message = $"{activeUsers[0].UserName} is already accessing this contract.  Please coordinate with them in order to prevent duplicate/overridden work.";
                    await Clients.Caller.SendAsync("notifyCaller", message);
                }
                else if (count > 2)
                {
                    message = $"{activeUsers[0].UserName} and others are already accessing this contract. Please coordinate with them in order to prevent duplicate/overriden work";
                    await Clients.Caller.SendAsync("notifyCaller", message);
                }
            }
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            activeUsers.Remove(activeUsers.Where(a => a.ConnectionId == Context.ConnectionId).FirstOrDefault());

            return null;
        }
    }
}

и вот мое промежуточное ПО ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Contract.API.Hubs
{
    public class SignalRMiddleware
    {
        private readonly RequestDelegate _next;

        public SignalRMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var request = httpContext.Request;

            // web sockets cannot pass headers so we must take the access token from query param and
            // add it to the header before authentication middleware runs
            if (request.Path.StartsWithSegments("/contractHub", StringComparison.OrdinalIgnoreCase) &&
                request.Query.TryGetValue("access_token", out var accessToken))
            {
                request.Headers.Add("Authorization", $"Bearer {accessToken}");
            }

            await _next(httpContext);
        }
    }
}

Код добавляется в концентратор в строке 25 с исключением ссылки Null. (UserInfo appUser = _userInfoQuery.GetUserInfo ();) Вот код этой функции только для обеспечения полного охвата ...

 public UserInfo GetUserInfo()
        {
            var userName = _requestContext.Requestor.Identity.Name;
            var displayName = _requestContext.Requestor.Claims.Where(f => f.Type == "name").Select(s => s.Value).FirstOrDefault() ?? _requestContext.Requestor.Identity.Name;
            var dbUser = _contractContext.Users
                .Include(i => i.Roles)
                .Include(i => i.FirewallWhiteList)
                .Include(i => i.AssignedStatus)
                .FirstOrDefault(f => f.UserName == userName);

            return new UserInfo(userName, displayName, dbUser);
        }

Я перепробовал все, что смог найти. пожалуйста, помогите!

1 Ответ

0 голосов
/ 28 января 2020

Вы используете DI в своем концентраторе и вводите экземпляр IUserInfoQuery. Глядя на ваш Startup я не вижу ни одной регистрации этой услуги. Вам необходимо зарегистрироваться:

services.AddTransient<IUserInfoQuery, UserInfoQuery>();
...