.net core3.1中signalr搭配JWT的简单使用,私聊,群聊,vue前后分离

5 篇文章 0 订阅
1 篇文章 0 订阅
  • 单对单私聊,网上查了很多资料,都没效果,要么不可取,如果不知道的JWT的,建议你先学会用JWT。有不对的地方望指出,互相学习
    我看到有人把一个用户当着一个组,用来单对单私聊,效果是可以的,但是我个人觉得这样不对,我的方法是初始化时,把用户id跟signalr的connectionid存放到一个字典中,私聊时,传入私聊对象的用户Id,然后根据字典获取到该对象的connectionid,发送过去就可以了

1,首先引入signalr,nuget中搜索安装就好,这里不多介绍,Microsoft.AspNetCore.SignalR.Common,不压缩的话就这个包就好

2,startup配置,ConfigureServices中

   services.AddSignalR().AddMessagePackProtocol();

Configure中,为了给前端signalr请求加上token,token的定义可能不一样,你们自己定义

 app.Use((context, next) =>
            {
                if (context.Request.Query.TryGetValue("Bearer", out var token))
                    context.Request.Headers.Add("Authorization", $"Bearer {token}");
                return next.Invoke();
            });

3,创建ChatHub类,继承Hub,注意要加上 [Authorize]验证特性

namespace AntAdmnin.Hubs
{
   [Authorize]
    public class ChatHub: Hub
    {
        //发送消息--发送给所有连接的客户端
        //public async Task SendMessage(string msg)
        //{
        //    await Clients.All.SendAsync("ReceiveMessage", msg);
        //}

        发送消息--发送给指定用户
        //public async Task SendPrivateMessage(string userId, string message)
        //{

        //    await Clients.User(userId).SendAsync("ReceiveMessage", message);
        //}

        public override async Task OnConnectedAsync()
        {
            //await Clients.All.OnNotify(new { UserId= Context.User.Identity.Name, Name=Context.User.Identity.Name, ConnectId = Context.ConnectionId });
            var userId = Context.User.Identity.Name;
          //把上线的用户跟signalr connectionid保存起来 
            SignalrHelper.UserAndSignalrDc[userId] =Context.ConnectionId;
            // 思路  创建一个组时 ,把用户的connectionid跟组名添加进signalr ,如果是持久性保存的组,可初始化时执行一遍组保存
            // 例如这里把所有的用户添加到一个叫 company的组  ,要先初始化用户关联字典

            foreach (var item in SignalrHelper.UserAndSignalrDc)
            {
                Groups.AddToGroupAsync(item.Value, "company");
            }
            await base.OnConnectedAsync();
        }

    }
}

4,创建一个字典,用来保存用户跟signalr连接id的关联,这应该是第三步才对,在上面signalr连接的时候保存数据

  public  class SignalrHelper
    {
        /// <summary>
        /// key是用户Id ,value 是connectionid
        /// </summary>
        public static Dictionary<string, string> UserAndSignalrDc = new Dictionary<string, string>();
    }

5,创建一个api控制器,ChatController,我这里用了autofac注入,不用的自己实例化就好,也可以不用控制器,直接请求chathub也是可以的

	  [Authorize]
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class ChatController : ControllerBase
    {

        private IHubContext<ChatHub> chatHub;

        public ChatController(IHubContext<ChatHub> chatHub)
        {
            this.chatHub = chatHub;
        }

        [HttpPost]
        public async Task<IActionResult> SendAllMsg(ChatRecordInput input)
        {
            try
            {
		chatHub.Clients.Group(input.Receiver.ToString()).SendAsync("ReceiveMessage", input.Msg);
                await chatHub.Clients.All.SendAsync("ReceiveMessage", input.Msg);
                return Ok(Result.Success());
            }
            catch (Exception ex)
            {

                throw ex;
            }

         
        }

        [HttpPost]
        public async Task<IActionResult> SendGroupMsg(ChatRecordInput input)
        {
            try
            {

                await chatHub.Clients.Group("company").SendAsync("ReceiveMessage", input.Msg);
                return Ok(Result.Success());
            }
            catch (Exception ex)
            {

                throw ex;
            }


        }

        [HttpPost]
        public async Task<IActionResult> SendUserMsg(ChatRecordInput input)
        {
            try
            {
             
                var connectionId = SignalrHelper.UserAndSignalrDc[input.Receiver.ToString()];
               await  chatHub.Clients.Clients(connectionId).SendAsync("ReceiveMessage", input.Msg);
                return Ok(Result.Success());
            }
            catch (Exception ex)
            {

                throw ex;
            }


        }

后端就是这样了,接下来是前端,用的vue
7, 同样先安装包 ,我这里用的yarn,npm的安装方式我试过好像安装不上的,不知道为什么,安装这个包花了很久,各种安装不上,知道的大佬说一下怎么回事

yarn add @aspnet/signalr

8,页面,ui我用的是ant, 大家可以只参考js部分,前后端的接收方法名得保持一致,这个应该都知道吧

<template>
  <div id="components-layout-demo-basic">
    <a-layout>
      <a-layout-sider>
        <a-list itemLayout="horizontal" :dataSource="friends">
          <a-list-item
            @click="chooseFriend(item.userId)"
            class="fItem"
            slot="renderItem"
            slot-scope="item"
          >
            <a-list-item-meta
              description="Ant Design, a design language for background applications"
            >
              <p class="nick" slot="title">{{ item.nickName }}</p>
              <a-avatar slot="avatar" :src="avatarFilter(item.avart)" />
            </a-list-item-meta>
          </a-list-item>
        </a-list>
      </a-layout-sider>
      <a-layout>
        <a-layout-content>
          <div style="height:370px; width:98%; background: #fff; margin:5px; text-align: center;">
            <p style="color:red">{{ remsg }}</p>
          </div>
        </a-layout-content>
        <a-layout-footer>
          <a-textarea v-model="msg" placeholder="enter message" :rows="4" />
          <a-button @click="sendMsg" type="primary">发送信息</a-button>
        </a-layout-footer>
      </a-layout>
    </a-layout>
  </div>
</template>
<script>
import { getFriendList, sendMsgPrivate } from '@/api/chat'
import { getImgUrl } from '@/utils/util'
import * as signalR from '@microsoft/signalr'
const signalRMsgPack = require('@aspnet/signalr-protocol-msgpack')
let connection = null
export default {
  data () {
    return {
      friends: [],
      sendUserId: '',
      msg: '',
      remsg: ''
    }
  },
  created () {
    this.fetchData()
  },
  computed: {
    avatarFilter () {
      return function (avatar) {
        return getImgUrl(avatar)
      }
    }
  },
  mounted () {
    console.log('开始连接' + this.$store.getters.token)
    connection = new signalR.HubConnectionBuilder()
      .withAutomaticReconnect()
      .withHubProtocol(new signalRMsgPack.MessagePackHubProtocol())
      .withUrl(process.env.VUE_APP_API_BASE_URL + '/chatHub?Bearer=' + this.$store.getters.token, { accessTokenFactory: () => 'Bearer ' + this.$store.getters.token })
      .build()
    connection.start().catch(err => alert(err.message))
    var _this = this
    // 实现Show方法
    connection.on('ReceiveMessage', function (username, message) {
      _this.remsg = _this.remsg + '<br>' + username + ':' + message
    })
  },
  methods: {
    fetchData () {
      getFriendList().then(response => {
        console.log(response)
        this.friends = response
      })
    },
    sendMsg () {
      if (this.msg.trim() === '') {
        alert('不能发送空白消息')
        return
      }
      var para = {
        receiver: this.sendUserId,
        msg: this.msg
      }
      console.log('私聊')
      sendMsgPrivate(para).then(response => {})
      // 调用后端方法 SendMsg 传入参数
      // connection.invoke("SendMsg", this.user, this.msg);
      this.msg = ''
    },
    chooseFriend (userId) {
      this.sendUserId = userId
    }
  }
}
</script>
<style>
#components-layout-demo-basic {
  text-align: center;
}
#components-layout-demo-basic .ant-layout-header,
#components-layout-demo-basic .ant-layout-footer {
  background: #7dbcea;
  color: #fff;
  height: 100px;
}
#components-layout-demo-basic .ant-layout-footer {
  line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-sider {
  background: #3ba0e9;
  color: #fff;
  line-height: 500px;
}
#components-layout-demo-basic .ant-layout-content {
  background: rgba(16, 142, 233, 1);
  color: #fff;
  min-height: 400px;
  line-height: 120px;
}
#components-layout-demo-basic > .ant-layout {
  margin-bottom: 48px;
}
#components-layout-demo-basic > .ant-layout:last-child {
  margin: 0;
}
.nick {
  color: red;
}
.fItem {
  margin-top: 5px;
}
</style>

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JWT 进行身份验证时,通常会设置一个过期时间,以确保用户在一段时间内保持登录状态。当 JWT 过期时,用户需要重新登录并获取新的 JWT 令牌。为了避免用户频繁重新登录,我们可以使用刷新令牌的方式来延长用户的登录状态。 在 .NET Core 3.1 ,我们可以使用 JwtSecurityTokenHandler 类来生成和验证 JWT 令牌。下面是一个简单JwtHelper 类的实现,它支持生成、刷新和验证 JWT 令牌: ```csharp using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; public class JwtHelper { private static readonly string secret = "your_secret_key_here"; private static readonly string issuer = "your_issuer_here"; private static readonly int expireMinutes = 30; private static readonly int refreshExpireMinutes = 60; public static string GenerateToken(string userId) { var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId) }; var tokenDescriptor = new SecurityTokenDescriptor { Issuer = issuer, Audience = issuer, Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddMinutes(expireMinutes), SigningCredentials = credentials }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } public static string RefreshToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); try { var claimsPrincipal = tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidIssuer = issuer, ValidAudience = issuer, IssuerSigningKey = securityKey, ValidateLifetime = false }, out SecurityToken validatedToken); var jwtToken = validatedToken as JwtSecurityToken; if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) { throw new SecurityTokenException("Invalid token"); } var userId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value; return GenerateToken(userId); } catch(Exception ex) { throw new SecurityTokenException("Invalid token", ex); } } public static bool ValidateToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); try { var claimsPrincipal = tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidIssuer = issuer, ValidAudience = issuer, IssuerSigningKey = securityKey, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = validatedToken as JwtSecurityToken; if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) { throw new SecurityTokenException("Invalid token"); } return true; } catch(Exception) { return false; } } } ``` 在上面的代码,我们定义了一个静态的 secret 字符串来存储 JWT 的密钥,一个 issuer 字符串来存储 JWT 的发行者,以及一个 expireMinutes 和 refreshExpireMinutes 来分别设置 JWT 令牌和刷新令牌的过期时间。在 GenerateToken 方法,我们使用 JwtSecurityTokenHandler 类来创建一个 JWT 令牌,并设置其过期时间和签名凭证。在 RefreshToken 方法,我们首先验证传入的 JWT 令牌是否有效,并提取其的用户 ID。然后,我们使用 GenerateToken 方法来生成一个新的 JWT 令牌。在 ValidateToken 方法,我们验证传入的 JWT 令牌是否有效,并返回一个布尔值表示验证结果。 使用 JwtHelper 类非常简单,只需要调用其的 GenerateToken、RefreshToken 和 ValidateToken 方法即可。下面是一个示例: ```csharp var token = JwtHelper.GenerateToken("user_id"); var isValid = JwtHelper.ValidateToken(token); var refreshedToken = JwtHelper.RefreshToken(token); ``` 需要注意的是,由于 JWT 令牌是无状态的,因此在刷新令牌时,我们需要使用一些外部存储机制(如数据库或缓存)来存储每个用户的刷新令牌。当用户请求刷新令牌时,我们可以从外部存储检索出用户的刷新令牌,并验证其有效性后生成新的 JWT 令牌。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值