ASP.NET Core SignalR 使用 Azure AD 返回 401 未经授权

Mar*_*son 6 c# signalr azure-active-directory asp.net-core angular

我有一个 SPA (Angular 7) 和一个 API (.Net Core),我使用 Azure AD 对其进行身份验证。我正在使用adal-angular4将我的角度应用程序与 AAD 集成。

一切都很好,但我也使用 SignalR 和 API 作为服务器,当我尝试从 SPA 连接时,我在协商“请求”上收到 401 Unauthorized 消息,并在响应标头中得到此消息:

响应头

该请求在 Authorization 标头中包含我的 Bearer 令牌,当我通过jwt.io运行该令牌时,我可以看到“aud”值是我的 SPA 的 Azure AD ClientId。

对 API 的所有常规请求都包含相同的令牌,我对此没有任何问题。我的所有控制器和集线器上都有[授权],但只有 SignalR 集线器会导致此问题。

我的服务器启动:

public Startup(IConfiguration configuration, IHostingEnvironment env)
{
    Configuration = configuration;
    _env = env;
}

public IConfiguration Configuration { get; }
private IHostingEnvironment _env;
public void ConfigureServices(IServiceCollection services)
{

    StartupHandler.SetupDbContext(services, Configuration.GetConnectionString("DevDb"));


    // Setup Authentication
    services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
        .AddAzureADBearer(options =>
        {
            Configuration.Bind("AzureAD", options);


        });

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    // Add functionality to inject IOptions<T>
    services.AddOptions();

    // Add AzureAD object so it can be injected
    services.Configure<AzureAdConfig>(Configuration.GetSection("AzureAd"));

    services.AddSignalR(options =>
    {
        options.EnableDetailedErrors = true;
        options.KeepAliveInterval = TimeSpan.FromSeconds(10);
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseDeveloperExceptionPage();
        app.UseHsts();
    }

    app.UseCookiePolicy();

    app.UseHttpsRedirection();

    //app.UseCors("AllowAllOrigins");
    app.UseCors(builder =>
    {
        builder.AllowAnyOrigin();
        builder.AllowAnyMethod().AllowAnyHeader();
        builder.AllowCredentials();
    });


    app.UseAuthentication();

    app.UseSignalR(routes => routes.MapHub<MainHub>("/mainhub"));

    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider(Path.Combine(_env.ContentRootPath, "Files")),
        RequestPath = new PathString("/Files")
    });

    app.UseMvc();
}
Run Code Online (Sandbox Code Playgroud)

我的 SignalR 中心:

[Authorize]
public class MainHub : Hub
{
    private readonly IEntityDbContext _ctx;

    public MainHub(IEntityDbContext ctx)
    {
        _ctx = ctx;
        _signalRService = signalRService;
    }

    public override Task OnConnectedAsync()
    {
        return base.OnConnectedAsync();
    }

    public override Task OnDisconnectedAsync(Exception exception)
    {
        return base.OnDisconnectedAsync(exception);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的 Angular 客户端上的 SignalRService。我在 app.component.ts 的构造函数中运行 startConnection() 。

export class SignalRService {
    private hubConnection: signalR.HubConnection;

    constructor(private adal: AdalService) {}

    startConnection(): void {
        this.hubConnection = new signalR.HubConnectionBuilder()
            .withUrl(AppConstants.SignalRUrl, { accessTokenFactory: () => this.adal.userInfo.token})
            .build();

        this.hubConnection.serverTimeoutInMilliseconds = 60000;

        this.hubConnection.on('userConnected', (user) => 
        {
            console.log(user);
        });

        this.hubConnection.start()
            .then(() => console.log('Connection started'))
            .catch(err => 
            {
                console.log('Error while starting connection: ' + err);
            });
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试过这个解决方案,但我也无法让它发挥作用。

编辑

当我实现了官方文档中的解决方案后,API 也停止处理常规请求,然后我返回:

未找到签名密钥

我已经用 填充了该IssuerSigningKey属性。我在这里做错了什么吗?TokenValidationParametersnew SymmetricSecurityKey(Guid.NewGuid().ToByteArray());

/编辑

当 API 接受我的访问令牌时,为什么 SignalR 不接受它?

Nan*_* Yu 2

验证访问令牌的签名时,您应该获取公钥,因为 Azure AD 可能使用一组特定的公钥-私钥对中的任何一个对令牌进行签名,密钥可以在以下位置找到:

\n\n
https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration \n
Run Code Online (Sandbox Code Playgroud)\n\n

在 JSON 响应中,您\xe2\x80\x99 将看到一个属性jwks_uri,该属性是包含 Azure AD 的 JSON Web 密钥集的 URI。与 jwt token 中的声明匹配kid,您可以找到 AAD 用于使用非对称加密算法(例如默认的 RSA 256)对令牌进行签名的密钥。

\n\n

在asp.net core api中,当验证Azure AD颁发的访问令牌时,您可以使用AddJwtBearer扩展并提供正确的Authority,以便中间件能够正确地从Azure AD OpenID配置端点获取密钥:

\n\n
options.Authority = "https://login.microsoftonline.com/yourtenant.onmicrosoft.com/"\n
Run Code Online (Sandbox Code Playgroud)\n\n

另一种选择是使用库AddAzureADBearer中的扩展Microsoft.AspNetCore.Authentication.AzureAD.UI。您还应该设置正确authority(instance + domain),中间件将帮助根据您的配置验证签名和声明。

\n