signalr是以group、connect为核心来进行推送,比如,给某个组、某个连接来推送,但实际场景中,核心应该是某个组、某个人;然而一个人可以对应多个连接(浏览器多个tab页);本节就来介绍下自行管理人、组、连接这些关系
由于signalr连接的时候不那么方便附带header和cookie(因为推送独立成一个子系统了),实际实现中采用以url query的形式附带上token,然后服务器端自定义解析token得到用户信息;

服务器端实现

  • configureservices中添加服务相关方法,代码如下,完整代码

    public void configureservices(iservicecollection services)
    {
        var appsection = configuration.getsection("app");
        services.configure<appsetting>(option => appsection.bind(option));
        var appsetting = appsection.get<appsetting>();
    
        services.addsingleton<signalrredishelper>();
    
        // services.addhostedservice<clearbackgroundservice>();
    
        services.addauthentication(options =>
        {
            options.defaultauthenticatescheme = jwtbearerdefaults.authenticationscheme;
            options.defaultchallengescheme = jwtbearerdefaults.authenticationscheme;
            options.defaultforbidscheme = jwtbearerdefaults.authenticationscheme;
        })
        .addjwtbearer(option =>
        {
            option.securitytokenvalidators.clear();
            option.securitytokenvalidators.add(new usertokenvalidation()); ;
    
            option.events = new jwtbearerevents()
            {
                onmessagereceived = context =>
                {
                    var userid = context.request.query["userid"].firstordefault();
                    if (!string.isnullorwhitespace(userid))
                    {
                        context.token = userid;
                    }
                    return task.completedtask;
                }
            };
        });
    
        services.addcors(options => options.addpolicy(corspolicy, builder =>
        {
            builder
                  .setisoriginallowedtoallowwildcardsubdomains()
                  .withorigins(appsetting.cors.split(","))
                  .allowanymethod()
                  .allowcredentials()
                  .allowanyheader()
                  .build();
        }));
    
        services.addcontrollers()
            .addnewtonsoftjson(options => options.serializersettings.contractresolver = new camelcasepropertynamescontractresolver())
            .configureapibehavioroptions(options =>
            {
                options.invalidmodelstateresponsefactory = context =>
                {
                    var result = new badrequestobjectresult(context.modelstate);
                    result.contenttypes.add(mediatypenames.application.json);
                    // result.contenttypes.add(mediatypenames.application.xml);
    
                    return result;
                };
            })
            .setcompatibilityversion(compatibilityversion.version_3_0);
    
        // 添加signalr
        services.addsignalr(config =>
        {
            if (_webenv.isdevelopment())
            {
                config.enabledetailederrors = true;
            }
        })
        // 支持messagepack
        .addmessagepackprotocol()
        // 使用redis做底板 支持横向扩展 scale-out
        .addstackexchangeredis(o =>
          {
              o.connectionfactory = async writer =>
              {
                  var config = new configurationoptions
                  {
                      abortonconnectfail = false,
                      // password = "changeme",
                      channelprefix = "__signalr_",
                  };
                  //config.endpoints.add(ipaddress.loopback, 0);
                  //config.setdefaultports();
                  config.defaultdatabase = appsetting.signalrrediscache.databaseid;
                  var connection = await connectionmultiplexer.connectasync(appsetting.signalrrediscache.connectionstring, writer);
                  connection.connectionfailed += (_, e) =>
                  {
                      console.writeline("connection to redis failed.");
                  };
    
                  if (connection.isconnected)
                  {
                      console.writeline("connected to redis.");
                  }
                  else
                  {
                      console.writeline("did not connect to redis");
                  }
    
                  return connection;
              };
          });
    }

    其中,signalrredishelper 为redis辅助方法,详情请参见
    usertokenvalidation 为自定义token解析方法,详情请参见,由于历史遗留问题,此处直接使用了userid,建议的做法是传递jwttoken,然后服务器端解析jwt token得到用户信息

hub中跟用户关联

在hub中通过context.user.identity.name可以获取到解析的值,通过这种关系来跟用户关联上,当然,也可以自定义修改使用其他信息,比如email或其他自定义的名称,具体请google

更多内容请通过快速导航查看下一篇

快速导航

标题 内容
索引 .net core 3.0 signalr – 实现一个业务推送系统
上一篇 .net core 3.0 signalr – 04 使用redis做底板来支持横向扩展
下一篇 .net core 3.0 signalr – 06 业务实现-业务分析
源码地址 源码
官方文档 官方文档