wtm plus(.NET6)SignalR使用的身份验证和授权(cookie+jwt)的正确姿势

wtm plus(.NET6)SignalR使用的身份验证和授权(cookie+jwt)的正确姿势,第1张

最近,需要开发即时通讯功能。于是,微软的实时通讯神器SignalR就是最好的选择。由于需要支持所有人发送、群聊和私聊。这样对于在hub的中获取用户身份就显得十分必要了。在wtm中我们采用itcode + ConnId的方式,缓存所有的连接,在私聊的时候用itcode找到对应的连接id就行了,这样可以有效的客户端防止刷新后,消息发送不正常的bug。具体的做法就不写了,其实用websocket也是这么干的。重点是如何在wtm的项目中,正确的通过cookie和jwt获取用户信息。

一、cookie方式

这个没啥好说的,正常的mvc项目中(wtm是layui)直接按照官方文档写js就行了,由于你自己继承的hub类,我这里是ChatHub: Hub,系统默认帮你注册过了。因此,咱们只需要通过在ChatHub构造函数中获取WTMContext就可以很舒服的拿到当前用户登录信息LoginUserInfo或者EFDataContext。具体还可以拿到什么,参照下图。详细看WTM官方的章节。

获取WTMContext写法就是,是不是简单粗暴。

public class ChatHub : Hub
    {


        private WTMContext _wTMContext { get; set; }
        public ChatHub(WTMContext wTMContext)
        {
            _wTMContext = wTMContext;
        }    
    }

使用的话,和控制器构造函数中获取的服务试用方法是一样的。

var itcode = _wTMContext.LoginUserInfo.ITCode;
二、JWT方式

 这个方法就是比较麻烦了,首先wtm plus默认注入的是AddWtmAuthentication,方法位于WalkingTec.Mvvm.Mvc命名空间下FrameworkServiceExtension静态类中。查看具体方法,对照微软的官网,我们发现,AddWtmAuthentication中只是缺少JwtBearerEvents关于SignalR的认证配置。也就是下面这一段。

 // Sending the access token in the query string is required due to
      // a limitation in Browser APIs. We restrict it to only calls to the
      // SignalR hub in this code.
      // See https://docs.microsoft.com/aspnet/core/signalr/security#access-token-logging
      // for more information about security considerations when using
      // the query string to transmit the access token.
      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;
          }
      };

1、修改WtmAuthentication方法

我们新建一个MyFrameworkServiceExtension静态类,把创建AddMyWtmAuthentication静态方法,该方法先完全复制wtm的AddWtmAuthentication方法,然后把上面这点代码加进入。options.Authority = "Authority URL";这个得重点说下,官方文档上是有的,我把他注释了,这个坑货加上后强制要求https。

全部代码如下

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Threading.Tasks;
using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Mvc.Auth;

namespace InnovationAlliance.Extension
{
    public static class MyFrameworkServiceExtension
    {
        public static IServiceCollection AddMyWtmAuthentication(this IServiceCollection services, IConfiguration config)
        {
            Configs configs = config.Get();
            services.AddScoped();
            JwtOption jwtOptions = configs.JwtOptions;
            CookieOption cookieOptions = configs.CookieOptions;
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            services.AddAuthentication("Cookies").AddJwtBearer("Bearer", delegate (JwtBearerOptions options)
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role",
                    ValidateIssuer = true,
                    ValidIssuer = jwtOptions.Issuer,
                    ValidateAudience = true,
                    ValidAudience = jwtOptions.Audience,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SecurityKey)),
                    ValidateLifetime = true
                };
                //options.Authority = "Authority URL";
                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;
                    }
                };


            }).AddCookie("Cookies", delegate (CookieAuthenticationOptions options)
            {
                options.Cookie.Name = CookieAuthenticationDefaults.CookiePrefix + "WTM.CookieWithJwtAuth";
                options.Cookie.HttpOnly = true;
                options.Cookie.SameSite = SameSiteMode.Strict;
                options.Cookie.Domain = (string.IsNullOrEmpty(cookieOptions.Domain) ? null : cookieOptions.Domain);
                options.ClaimsIssuer = cookieOptions.Issuer;
                options.SlidingExpiration = cookieOptions.SlidingExpiration;
                options.ExpireTimeSpan = TimeSpan.FromSeconds(cookieOptions.Expires);
                options.LoginPath = cookieOptions.LoginPath;
                options.LogoutPath = cookieOptions.LogoutPath;
                options.ReturnUrlParameter = cookieOptions.ReturnUrlParameter;
                options.AccessDeniedPath = cookieOptions.AccessDeniedPath;
            });
            return services;
        }

    }
}

2、给ChatHub打上wtm的身份认证特性标签

    [AuthorizeJwtWithCookie]
    public class ChatHub : Hub
    {
        private WTMContext _wTMContext { get; set; }
        public ChatHub(WTMContext wTMContext)
        {
            _wTMContext = wTMContext;
        }    
    }

3、js客户端上accessTokenFactory方法传递access_token,access_token就是那种没有Bearer的那种。一下是一段uniapp使用jwt的代码。

let token = uni.getStorageSync('token');
			 this.connection = new HubConnectionBuilder()
				.withUrl("http://localhost:48936/chathub", {
					accessTokenFactory: () => {
					    return token.replace ('Bearer ',"");
					}
				})
				.configureLogging(LogLevel.Trace)
				.build();


			this.start();
			this.connection.on("FriendsOnline", function(message) {
				console.log("收到的消息" + message);
			});

			this.connection.onclose(function() { //去掉斜杠
				console.log("websocket连接断开");
				//重试
				this.start();
			});

4、在 Startup中,用自己写的AddMyWtmAuthentication,替换原来的AddWtmAuthentication

services.AddMyWtmAuthentication(ConfigRoot);

经过一天的踩坑调试终于成功了。

用wtmplus,对于新手来说是个很好的过度时期,在线可视化制作项目。同时,还能得到微软MVP刘亮的一对一指导,可以在一个相对舒服的环境里完成项目。

有兴趣的可以去wtmplus官网,体验一下.NET在线开发。
传送门http://wtmplus.walkingtec.cn/index.html#/
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/891764.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)

保存