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

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

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

https://img.mukewang.com/650fff510001e95106510023.jpg

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

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

我的 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);

    }

}

这是我的 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);

            });

    }

}

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



慕斯709654
浏览 134回答 1
1回答

泛舟湖上清波郎朗

您需要对 JWT Bearer 事件进行特殊处理,以便您的身份验证正常工作。令牌需要转发到集线器。看看我说的部分那部分不见了public void ConfigureServices(IServiceCollection services){    services.AddDbContext<ApplicationDbContext>(options =>        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));    services.AddIdentity<ApplicationUser, IdentityRole>()        .AddEntityFrameworkStores<ApplicationDbContext>()        .AddDefaultTokenProviders();    services.AddAuthentication(options =>        {            // Identity made Cookie authentication the default.            // However, we want JWT Bearer Auth to be the default.            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;        })        .AddJwtBearer(options =>        {            // Configure JWT Bearer Auth to expect our security key            options.TokenValidationParameters =                new TokenValidationParameters                {                    LifetimeValidator = (before, expires, token, param) =>                    {                        return expires > DateTime.UtcNow;                    },                    ValidateAudience = false,                    ValidateIssuer = false,                    ValidateActor = false,                    ValidateLifetime = true,                    IssuerSigningKey = SecurityKey                };            //THAT IS THE PART WHICH IS MISSING IN YOUR CONFIG !            // We have to hook the OnMessageReceived event in order to            // allow the JWT authentication handler to read the access            // token from the query string when a WebSocket or             // Server-Sent Events request comes in.            options.Events = new JwtBearerEvents            {                OnMessageReceived = context =>                {                    var accessToken = context.Request.Query["access_token"];                    // If the request is for our hub...                    var path = context.HttpContext.Request.Path;                    if (!string.IsNullOrEmpty(accessToken) &&                        (path.StartsWithSegments("/hubs/chat")))                    {                        // Read the token out of the query string                        context.Token = accessToken;                    }                    return Task.CompletedTask;                }            };        });    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);    services.AddSignalR();    // Change to use Name as the user identifier for SignalR    // WARNING: This requires that the source of your JWT token     // ensures that the Name claim is unique!    // If the Name claim isn't unique, users could receive messages     // intended for a different user!    services.AddSingleton<IUserIdProvider, NameUserIdProvider>();    // Change to use email as the user identifier for SignalR    // services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();    // WARNING: use *either* the NameUserIdProvider *or* the     // EmailBasedUserIdProvider, but do not use both. }
打开App,查看更多内容
随时随地看视频慕课网APP