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

ASP.NET Core SignalR身份验证

在需要登录才能访问的集线器类上或者方法上添加[Authorize]。也支持角色等设置,可以设置到Hub或者方法上。

配置好User、Role、MyDbContext、JWTSettings、IdentityHelper

Program.cs

using SignaIR的基本使用;
using Scalar.AspNetCore;
using Identity框架;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using System.Text;var builder = WebApplication.CreateBuilder(args);builder.Services.AddControllers();
builder.Services.AddOpenApi();//添加数据库上下文
builder.Services.AddDbContext<MyDbContext>(opt =>
{string connStr = Environment.GetEnvironmentVariable("ConnStr");opt.UseSqlServer(connStr);
});//添加Identity服务
builder.Services.AddDataProtection();
builder.Services.AddIdentityCore<MyUser>(options =>
{//设置密码规则,不需要数字,小写字母,大写字母,特殊字符,长度为6options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(30);options.Password.RequireDigit = false;options.Password.RequireLowercase = false;options.Password.RequireUppercase = false;options.Password.RequireNonAlphanumeric = false;options.Password.RequiredLength = 6;options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
});
var idBuilder = new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services);
idBuilder.AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders().AddRoleManager<RoleManager<MyRole>>().AddUserManager<UserManager<MyUser>>();//添加JWT设置
builder.Services.Configure<JWTSettings>(builder.Configuration.GetSection("JWT"));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt =>
{var jwtOpt = builder.Configuration.GetSection("JWT").Get<JWTSettings>();byte[] key = Encoding.UTF8.GetBytes(jwtOpt.SecKey);//设置对称秘钥var secKey = new SymmetricSecurityKey(key);//设置验证参数opt.TokenValidationParameters = new(){ValidateIssuer = false,//是否验证颁发者ValidateAudience = false,//是否验证订阅者ValidateLifetime = true,//是否验证生命周期ValidateIssuerSigningKey = true,//是否验证签名IssuerSigningKey = secKey//签名秘钥};//设置事件opt.Events = new JwtBearerEvents{OnMessageReceived = context =>{//WebSocket不支持自定义报文头,所以把JWT通过url中的QueryString传递var accessToken = context.Request.Query["access_token"];var path = context.HttpContext.Request.Path;//如果是MyHub的请求,就在服务器端的OnMessageReceived中把QueryString中的JWT读出来赋值给context.Tokenif (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/MyHub"))){context.Token = accessToken;}return Task.CompletedTask;}};});
//SignalR
builder.Services.AddSignalR();
//跨域
string[] urls = new[] { "http://localhost:5173" };
builder.Services.AddCors(options =>options.AddDefaultPolicy(builder => builder.WithOrigins(urls).AllowAnyMethod().AllowAnyHeader().AllowCredentials()));var app = builder.Build();if (app.Environment.IsDevelopment())
{app.MapOpenApi();app.MapScalarApiReference();
}
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<MyHub>("/MyHub");
app.MapControllers();app.Run();

MyHub.cs

[Authorize]
public class MyHub : Hub
{public Task SendPublicMessage(string message){var claim = this.Context.User.FindFirst(ClaimTypes.Name);string connId = this.Context.ConnectionId;string msgToSend = $"{connId}{DateTime.Now}:{message}{claim.Value}";return Clients.All.SendAsync("ReceivePublicMessage", msgToSend);}
}

DemoController.cs

[Route("api/[controller]/[action]")]
[ApiController]
public class DemoController : ControllerBase
{private readonly UserManager<MyUser> userManager;private readonly IOptionsSnapshot<JWTSettings> jwtSettingsOpt;public DemoController(UserManager<MyUser> userManager, IOptionsSnapshot<JWTSettings> jwtSettingsOpt){this.userManager = userManager;this.jwtSettingsOpt = jwtSettingsOpt;}[HttpPost]public async Task<ActionResult<string>> Login(LoginRequest req){//根据用户名查找用户var user = await userManager.FindByNameAsync(req.UserName);if (user == null){return BadRequest("用户或密码错误1");}//判断是否登录成功,失败则记录失败次数if (await userManager.CheckPasswordAsync(user, req.Password)){//登录成功,重置失败次数,CheckAsync判断操作是否成功,失败则抛出异常await userManager.ResetAccessFailedCountAsync(user).CheckAsync();await userManager.UpdateAsync(user);//身份验证声明List<Claim> claims = new List<Claim>{new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),new Claim(ClaimTypes.Name, user.UserName),};//获取用户角色,添加到声明中var roles = await userManager.GetRolesAsync(user);foreach (var role in roles){claims.Add(new Claim(ClaimTypes.Role, role));}//生成JWTstring key = jwtSettingsOpt.Value.SecKey;DateTime expires = DateTime.Now.AddSeconds(jwtSettingsOpt.Value.ExpireSeconds);byte[] keyBytes = Encoding.UTF8.GetBytes(key);var secKey = new SymmetricSecurityKey(keyBytes);var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);var tokenDescriptor = new JwtSecurityToken(claims: claims,//声明expires: expires,//过期时间signingCredentials: credentials//签名凭据);string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);return jwt;}else{await userManager.AccessFailedAsync(user).CheckAsync();return BadRequest("用户或密码错误2");}}
}

Vue

<template><input type="text" v-model="state.userMessage" v-on:keypress="txtMsgOnkeypress" /><div>用户名<input type="text" v-model="state.loginData.username" />密码<input type="password" v-model="state.loginData.password" /><button v-on:click="loginClick">登录</button></div><div><ul><li v-for="(msg, index) in state.messages" :key="index">{{ msg }}</li></ul></div>
</template><script>
import { reactive } from 'vue';
import * as signalR from '@microsoft/signalr';
import axios from 'axios';let connection;
export default {name: 'Login',setup() {//创建响应式对象const state = reactive({accessToken: "", userMessage: "", messages: [],loginData: { userName: "", password: "" }});//SignalR连接const startConn = async function () {const transport = signalR.HttpTransportType.WebSockets;const options = { skipNegotiation: true, transport: transport };options.accessTokenFactory = () => state.accessToken;connection = new signalR.HubConnectionBuilder().withUrl('https://localhost:7181/MyHub', options).withAutomaticReconnect().build();try {await connection.start();} catch (err) {alert(err);return;}//注册ReceivePublicMessage事件,接收消息,添加到messages数组connection.on('ReceivePublicMessage', msg => {//监听服务器端发送过来的信息state.messages.push(msg);});}//点击登录const loginClick = async function () {const resp = await axios.post('https://localhost:7181/api/Demo/Login', state.loginData).then((response) => {state.accessToken = response.data;startConn()})};//按下回车键发送消息,调用SendPublicMessage方法,发送消息,清空输入框const txtMsgOnkeypress = async function (e) {if (e.keyCode != 13) return;await connection.invoke("SendPublicMessage", state.userMessage); state.userMessage = "";};//返回响应式对象和方法return { state, txtMsgOnkeypress, loginClick };},
}
</script>

http://www.lryc.cn/news/534517.html

相关文章:

  • 微信小程序(第一集)
  • 为什么细胞是圆的?
  • 游戏引擎学习第96天
  • 本地优先的分布式锁实现
  • 基于知乎平台的“开源AI智能名片2 + 1链动模式S2B2C商城小程序”引流策略研究
  • DeepSeek-Coder系列模型:智能编程助手的未来
  • FPGA开发技能(10)热电偶测温ADS1118方案
  • 如何优化网站结构以促进快速收录?
  • 算法-动态规划-0-1背包问题(二维0-1背包,背包求方案数,求背包具体方案)
  • 位运算算法篇:位运算实现加减乘除
  • 【故障处理】ORA-19849 ORA-19612 0RA-17627 ORA-03114
  • 【MQ】Spring3 中 RabbitMQ 的使用与常见场景
  • jupyterLab插件开发
  • 拯救者Y9000P双系统ubuntu22.04安装4070显卡驱动
  • QT-常见问题
  • 如何通过腾讯 ima.copilot 训练自己的知识库
  • 关于近期我的交流之深度思考DeepSeek归纳总结
  • 智能生鲜配送管理系统:生鲜及快消品行业的数字化转型利器
  • DeepSeek和ChatGPT的优劣或者区别(答案来DeepSeek和ChatGPT)
  • 【C语言标准库函数】标准输入输出函数详解[5]:格式化文件输入输出
  • [概率论] 随机变量
  • 中国通信企业协会通信网络安全服务能力评定安全设计与集成服务能力评定三级要求准则...
  • 【C++语言】类和对象(下)
  • 【Spring】什么是Spring?
  • 全面理解-c++11中的智能指针
  • 【jmeter】在windows中,创建的变量,在jmeter中,读取变量失败的问题,路径问题
  • 【CubeMX-HAL库】STM32F407—无刷电机学习笔记
  • 使用 POI-TL 和 JFreeChart 动态生成 Word 报告
  • xxl-job的分片广播
  • MobaXterm破解会话上限限制