【问题标题】:SignalR + Angular: how to add Bearer token to Http HeadersSignalR + Angular:如何将承载令牌添加到 Http 标头
【发布时间】:2018-06-15 14:10:48
【问题描述】:

我制作了一个使用 Bearer Token 进行身份验证的 asp.net core 2.0 SignalR Hub。现在我对如何通过 SignalR Angular 5 客户端连接它有点迷茫。如果我从 Hub 中删除授权,我实际上可以连接,因此连接正常,现在我相信我只需将 Authorization Bearer 添加到连接的 Http Headers 即可。

我的 Angular 5 项目的 package.json 文件中的 SignalR 客户端引用:"@aspnet/signalr-client": "^1.0.0-alpha2-final"

我的 Angular 组件:

import { Component, OnInit } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
import { AuthenticationService } from '../core/authentication/authentication.service';
import { HubConnection } from '@aspnet/signalr-client';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  quote: string;
  isLoading: boolean;
  jwtToken:string;
  private hubConnection: HubConnection;

  constructor(
    private _http: HttpClient, 
    private _auth : AuthenticationService,
    private _toastr: ToastrService) { }

  ngOnInit() {
    this.isLoading = false;
    this.jwtToken = this._auth.currentToken;

    this.hubConnection = new HubConnection('http://localhost:27081/hub/notification/');
    this.hubConnection
      .start()
      .then(() => console.log('Connection started!'))
     .catch(err => console.error('Error while establishing connection :(', err));        
    this.hubConnection.on("send", data => {
        console.log(data);
    });
  }

  showToastr(){
    this._toastr.success('Hello world!', 'Toastr fun!');
  }

}

由于阅读了类似的问题,我尝试了:this.hubConnection.Headers.Add("token", tokenValue); 但它不起作用,Headers 属性不存在。

如何将 Bearer 令牌添加到 HubConnection 的 Http Headers?

感谢您的帮助

【问题讨论】:

    标签: asp.net angular signalr.client


    【解决方案1】:

    要使用@aspnet/signalr (^1.1.4) 执行此操作,您可以使用以下代码

    const options: IHttpConnectionOptions = {
      accessTokenFactory: () => {
        return "Token is resolved here";
      }
    };
    
    const connection = new signalR.HubConnectionBuilder()
      .configureLogging(signalR.LogLevel.Information)
      .withUrl(`${environment.apiUrl}/notify`, options)
      .build();
    

    同时为您的 Hub 添加注释

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    

    附带说明,SignalR 在使用 websocket 协议时似乎没有将 Bearer 令牌作为标头附加,而是将其作为“access_token”参数添加到请求 URL,这需要您配置身份验证来处理此问题signalR 选择使用 ws 时的令牌。

    services.AddAuthentication(x =>
                {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(x =>
                {
                    x.RequireHttpsMetadata = false;
                    x.SaveToken = true;
                    x.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(key),
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                    x.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("/notify")))
                            {
                                // Read the token out of the query string
                                context.Token = accessToken;
                            }
                            return Task.CompletedTask;
                        }
                    };
                });
    

    【讨论】:

    • 这篇文章是唯一对我有用的东西,特别是设置承载事件选项的部分。我突然间只在 Chrome 中遇到了连接问题,但我没有触及我的聊天中心代码。我无法理解为什么这对我有用,但感谢您的分享!
    • @Daxxy 你能在这里详细说明Token is resolved here吗?令牌来自哪里?
    • @Daxxy 为什么我们需要这个? { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false }; 由于未定义key,我在IssuerSigningKey = new SymmetricSecurityKey(key) 这一行中收到错误消息
    • 我想实现 CORS,如何在 IHttpConnectionOptions 中传递 headers 和访问令牌?
    • @thodwris 密钥应该在别处定义,这部分代码是 JWT 令牌标准设置的一部分,与信号器问题无关。 signalr 实际需要的唯一部分是 JwtBearerEvents 部分,其余部分仅用于标准 jwt 设置,并且可以根据您在项目中的设置方式而有所不同。请记住,这只是简短的 sn-ps 代码,目的是让您了解为了实现这一点需要做什么。
    【解决方案2】:

    通过阅读他们的源代码和测试,您似乎可以提供一个包含访问令牌的选项对象,就像这样

    var options = {
        transport: transportType,
        logging: signalR.LogLevel.Trace,
        accessToken: function () {
            return jwtToken;
        }
    };
    
    hubConnection = new signalR.HubConnection('/authorizedhub', options);
    hubConnection.start();
    

    特别是测试文件中的代码here

    【讨论】:

    • 感谢您的回答!我无法用你的答案解决。我正在阅读,看起来您无法设置标题。我设法使用这篇文章解决了:damienbod.com/2017/10/16/…
    • 这很好用!在我的情况下,我必须在 Authorize attibute 中指定 AuthenticationScheme,即 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    【解决方案3】:

    我找不到使用 angular 解决的方法,但我在 this article 之后使用 asp.net 解决了问题。

    这就是我所做的:现在要连接,我在查询字符串中传递 jwt 令牌并指定传输类型:

    const options = {
          transport: TransportType.WebSockets
    };
    this.hubConnection = new HubConnection('http://localhost:27081/hub/notification/?token='+this.jwtToken, options);
    

    然后在startup.cs > ConfigureServices()

            services
                .AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(cfg =>
                {
                    cfg.RequireHttpsMetadata = false;
                    cfg.SaveToken = true;
                    cfg.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = Configuration["JwtIssuer"],
                        ValidAudience = Configuration["JwtIssuer"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
                        ClockSkew = TimeSpan.Zero // remove delay of token when expire
                    };
                    cfg.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context =>
                        {
                            if (context.Request.Query.TryGetValue("token", out StringValues token)
                            )
                            {
                                context.Token = token;
                            }
    
                            return Task.CompletedTask;
                        },
                        OnAuthenticationFailed = context =>
                        {
                            var te = context.Exception;
                            return Task.CompletedTask;
                        }
                    };
                });
    

    【讨论】:

    • 我已经更新了我的答案,说明如何使用 angular 正确执行此操作。您应该使用 accessTokenFactory 来解析您的令牌并将其作为选项传递。这样做时,它会将令牌作为“access_token”查询参数发送。
    • @André Luiz this.jwtToken 对我来说是未定义的
    猜你喜欢
    • 2019-12-20
    • 2019-11-29
    • 2021-07-23
    • 2013-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    相关资源
    最近更新 更多