当前位置: 首页 > news >正文

使用Vue 3与.NET 8.0通过SignalR实现实时通信,并结合JWT身份验证

实时通信是一个非常重要的功能。SignalR是一个强大的库,能够帮助我们轻松实现客户端和服务器之间的实时数据传输。本文将结合你的代码示例,向你展示如何使用Vue 3作为前端框架,ASP.NET Core作为后端框架,通过SignalR实现实时消息通信,并结合JWT(JSON Web Token)进行身份验证。

结合我上篇文章JWT校验,接下来简单描述一下,使用JWT进行SignalR身份验证,业务背景:比如网站登录成功后才能发送实时聊天信息等业务

目标

  1. 用户登录后获取JWT令牌。
  2. 使用JWT令牌建立安全的SignalR连接。
  3. 实现客户端发送消息到服务器,服务器广播消息到所有客户端。

技术栈

  1. 前端:Vue 3 + @microsoft/signalr
  2. 后端:ASP.NET Core 8.0 + SignalR + JWT身份验证
  3. 数据传输协议:WebSocket(SignalR默认协议)

对Vue.js和ASP.NET Core有一定的了解。
安装了Node.js和Visual Studio或者VS Code等开发环境。

后端Program.cs中添加依赖注入等服务

// 注册身份验证服务。
builder.Services.AddAuthentication(opt =>
{
// DefaultAuthenticateScheme 和 DefaultChallengeScheme:设置默认的身份验证方案为JwtBearer。opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opt =>
{var jwtSetting = builder.Configuration.GetSection("JWTSettingOption").Get<JWTSetting>();opt.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = false,ValidateAudience = false,ValidateLifetime = true,ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecretKey))};opt.Events = new JwtBearerEvents{// SignalR 当接收到消息时触发。 在SignalR场景下,JWT令牌通常通过查询字符串传递(因为WebSocket协议不支持HTTP头传递令牌)。//检查请求路径是否为/SignalRHub/MyHub,如果是,则从查询字符串中提取access_token并赋值给context.Token。OnMessageReceived = context =>{if (context.Request.Path.StartsWithSegments("/SignalRHub/MyHub")){var accessToken = context.Request.Query["access_token"];if (!string.IsNullOrEmpty(accessToken)){context.Token = accessToken;}}return Task.CompletedTask;}};
});
builder.Services.AddSignalR(); // 注册SignalR服务,net8.0不需下载SignalR包
// CORS的作用:
// CORS(Cross-Origin Resource Sharing)用于解决跨域问题。
// 默认情况下,浏览器会阻止跨域请求。通过配置CORS策略,我们可以允许特定的前端地址访问后端API。
// 配置策略:
// WithOrigins:指定允许的前端地址(例如http://localhost:5173)。
// AllowAnyHeader 和 AllowAnyMethod:允许所有请求头和方法。
// AllowCredentials:允许传递凭据(如cookies或JWT令牌)。
builder.Services.AddCors(options =>
{options.AddPolicy("AddCors", policy =>{policy.WithOrigins("http://localhost:5173") // 指定允许的前端地址  .AllowAnyHeader().AllowAnyMethod().AllowCredentials(); // 允许凭证传递(如 cookies)});
});var app = builder.Build();app.UseCors("AddCors"); // 必须在其他中间件之前调用,以确保跨域请求能够正确处理。
app.UseAuthentication(); // 启用身份验证中间件,检查传入的请求是否包含有效的JWT令牌
app.UseAuthorization(); // 启用授权中间件,确保用户具有访问特定资源的权限。

SignalR 的核心概念
Hub
Hub 是 SignalR 的核心组件,它充当服务器和客户端之间的通信桥梁。
客户端可以调用 Hub 上的方法,Hub 也可以向客户端发送消息。

客户端-服务器交互
客户端通过 SignalR JavaScript 库(或其他支持的客户端库)与服务器建立连接。
服务器可以通过 Hub 向所有客户端、特定客户端或分组广播消息。

传输方式:
SignalR 自动选择最佳的传输协议(WebSocket、Server-Sent Events 或长轮询)。

依赖注入
在 .NET Core 和 .NET 8.0 中,SignalR 与依赖注入系统深度集成,方便扩展和管理。

示例代码

创建一个 MyHub 类,继承自 Hub:

using Microsoft.AspNetCore.SignalR;// 标记该Hub需要身份验证(通过JWT令牌)。
[Authorize]
public class MyHub : Hub
{// 广播消息给所有客户端public async Task SendMessage(string message){await Clients.All.SendAsync("ReceiveMessage", user, message);}// 向调用者发送消息public async Task SendToCaller(string message){await Clients.Caller.SendAsync("ReceiveMessage", "System", message);}// 向特定用户发送消息public async Task SendToUser(string userId, string message){await Clients.User(userId).SendAsync("ReceiveMessage", "Private", message);}
}

vue3 示例代码

// 首先需要准备 
npm install @microsoft/signalr 
npm install axios

(1) 模板部分 (template)
功能说明:
输入框:用户可以在这里输入消息,按下回车键后会触发sendMessageOnEnter方法。
登录表单:用户输入用户名和密码,点击“登录”按钮后调用login方法。
消息列表:显示从服务器接收到的所有消息。

<div><h1>SignalR 实时消息</h1><div><inputtype="text"v-model="state.userMessage"@keypress="sendMessageOnEnter":disabled="!state.isConnected"placeholder="等待连接..."/></div><div><table><tr><td>登录:</td><td><input type="text" v-model="state.userName"/></td></tr><tr><td>密码:</td><td><input type="password" v-model="state.password"/></td></tr><tr><td><button v-on:click="login">登录</button></td></tr></table></div><ul><li v-for="(msg, index) in state.messages" :key="index">{{ msg }}</li></ul>
</div>

(2) 脚本部分 (script)
数据模型 (reactive)

这里定义了应用的状态管理对象state,用于存储用户输入、连接状态、消息列表等信息。

const state = reactive({userMessage: '', // 用户输入的消息messages: [],    // 接收到的消息列表isConnected: false, // 是否已连接到SignalR服务器userName: '',    // 用户名password: '',    // 密码token: '',       // JWT令牌
});

登录逻辑 (login)

功能说明:
检查用户名和密码是否为空。
发送登录请求到后端API。
如果登录成功,保存JWT令牌并调用startSignalRConnection方法启动SignalR连接

const login = async () => {if (!state.userName.trim() || !state.password.trim()) {alert("用户名和密码不能为空!");return;}try {const param = {username: state.userName,password: state.password,};await axios.post('https://localhost:7182/api/JWTTest/Login', param).then((response) => {if (response.data.code === 200) {alert("登录成功");state.token = response.data.token; // 存储JWT令牌startSignalRConnection();         // 开始SignalR连接} else {alert("登录失败");}});} catch (error) {alert("登录失败");console.error("登录失败:", error);}
};

SignalR连接初始化 (startSignalRConnection)

功能说明:
配置SignalR连接选项,包括跳过协商、使用WebSocket协议以及通过accessTokenFactory提供JWT令牌
定义事件监听器,处理连接状态变化(如重连、断开)。
监听ReceiveMessage事件,接收服务器广播的消息。

const startSignalRConnection = async () => {try {const options = {skipNegotiation: true,transport: signalR.HttpTransportType.WebSockets,accessTokenFactory: () => state.token, // 使用JWT令牌进行身份验证};connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:7182/SignalRHub/MyHub', options).withAutomaticReconnect().build();connection.onreconnecting(() => {console.log("SignalR 正在尝试重新连接...");state.isConnected = false;});connection.onreconnected(() => {console.log("SignalR 重新连接成功");state.isConnected = true;});connection.onclose(() => {console.error("SignalR 连接关闭");state.isConnected = false;});connection.on('ReceiveMessage', (message) => {state.messages.push(message); // 接收到服务器推送的消息});await connection.start();console.log("SignalR 连接成功");state.isConnected = true;} catch (err) {console.error("SignalR 连接失败:", err);}
};

发送消息 (sendMessageOnEnter)

功能说明:
检查是否按下了回车键以及输入框内容是否为空。
调用SignalR的invoke方法,将消息发送到服务器的SendMessage方法。

const sendMessageOnEnter = async (e) => {if (e.key !== 'Enter') return;if (!state.userMessage.trim()) return;if (!state.isConnected) {alert("SignalR 连接未就绪,请稍后再试!");return;}try {await connection.invoke('SendMessage', state.userMessage); // 调用服务器方法state.userMessage = ''; // 清空输入框} catch (err) {console.error("发送消息失败:", err);}
}

相关文章:

  • 十、自动化函数+实战
  • youtube视频和telegram视频加载原理差异分析
  • Uniapp 使用Android studio进行离线打包
  • 小白工具视频转 3GP,多格式转换与数据安全的完美结合,在线使用
  • 责任链模式(Chain of Responsibility Pattern)
  • python-将文本生成音频
  • React 入门教程:构建第一个 React 应用
  • 嵌入式C语言进阶(二+)内存管理补充版
  • nvm切换node版本后,解决npm找不到的问题
  • Web前端 (CSS篇)
  • MyBatis:SpringBoot结合MyBatis、MyBatis插件机制的原理分析与实战
  • Shell编程之正则表达式与文本
  • Tomcat大版本升级教程
  • B端可视化方案,如何助力企业精准决策,抢占市场先机
  • MyBatis与MyBatis-Plus:字段自动填充的两种实现方式
  • 【Netty篇】Future Promise 详解
  • 【物联网】基于LORA组网的远程环境监测系统设计
  • 医疗大模型落地方案:技术选型、部署策略与调优
  • 与/或形演绎推理——基于王永庆著《人工智能原理与方法》的深度解析
  • GitHub 趋势日报 (2025年04月15日)
  • 红河做网站/最好的小说网站排名
  • 网站流量排名 全球/上海关键词优化排名软件
  • 湖南省住房和城乡建设厅网站/什么软件可以排名次
  • 专门做教育咨询有限公司网站/域名申请的流程
  • 微信群二维码推广平台/网站seo在线诊断分析
  • 库尔勒网站建设公司/西安优化排名推广