.net core SignalR的基础使用和身份认证

  • SinglR理解:它就相当于路由器的作用;收集客户端的请求,将相应请求对应消息发,根据不同需求发回客户端
基本使用
  • 安装包:Microsoft.AspNetCore.SignalR.StackExchangeRedis
  • 在程序中注入服务:builder.Services.AddSignalR();添加hub类:app.MapHub<ChatRoomHub>("/ChatRoomHub");
  • 创建一个继承hub的类,代码如下
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;
using static signalRTest.UserManeger;

namespace signalRTest
{
// [Authorize]用于身份验证
    [Authorize]
    public class ChatRoomHub:Hub
    {
        public Task SendPublicMessage(string message)
        {
            string connId = this.Context.ConnectionId;
            string msg = $"{connId} {DateTime.Now}:{message}";
            return Clients.All.SendAsync("ReceivePublicMessage", msg);
        }
        public async Task<string> SendPrivateMessage(string name, string message)
        { 
           User? user=UserManeger.FindByName(name);
           if (user == null) { return "DestUserNotDefind"; }
           string destUserId=user.id.ToString();
            string srcSendUser = this.Context.User!.FindFirst(ClaimTypes.Name)!.Value;
            string time = DateTime.Now.ToString();
            await this.Clients.User(destUserId).SendAsync("ReceivePriveteMessage", srcSendUser, time, message);
            return "ok";
        }
    }
}

(后端工作完成)

  • 前端
<template>
    <input type="text"  v-model="state.userMessage" v-on:keypress="txtMsgOnkeypress"/>
    <div><ul>
        <li v-for="(msg,index) in state.messages" :key="index">{{msg}}</li>
    </ul></div>
</template>
<script>
    import { reactive, onMounted } from 'vue';
    import * as signalR from '@microsoft/signalr';
    let connection;
    export default {name: 'Login',
        setup() {
            const state = reactive({ userMessage: "", messages: [] });
            const txtMsgOnkeypress = async function (e) {
                if (e.keyCode != 13) return;
                //hub类中发消息的方法
                await connection.invoke("SendPublicMessage", state.userMessage);
                state.userMessage = "";
            };
            onMounted(async function () {
                connection = new signalR.HubConnectionBuilder()
                    .withUrl('https://localhost:44337/Hubs/ChatRoomHub')//自己的hub类的路径
                    .withAutomaticReconnect().build();
                await connection.start();
                //发送消息的key
                connection.on('ReceivePublicMessage', msg => {
                    state.messages.push(msg);
                });
            });
            return { state, txtMsgOnkeypress };
        },
    }
</script>
  • 前后端交互属于跨域,所以要启用中间件UseCors();
  • program.cs配置如下
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.IdentityModel.Tokens;
    using signalRTest;
    using System.Text;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    //注入jwt;
    builder.Services.Configure<JWTOption>(builder.Configuration.GetSection("JWT"));
    //将jwt引入到swagger
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(x =>
    {
        var jwtOpt = builder.Configuration.GetSection("JWT").Get<JWTOption>();
        byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOpt.SigningKey);
        var secKey = new SymmetricSecurityKey(keyBytes);
        x.TokenValidationParameters = new()
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = secKey
        };
        //从请求头中拿到Token,用于身份验证
        x.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                var accessToken = context.Request.Query["access_token"];
                var path = context.HttpContext.Request.Path;
                if (!string.IsNullOrEmpty(accessToken) &&
                    (path.StartsWithSegments("/ChatRoomHub")))
                {
                    context.Token = accessToken;
                }
                return Task.CompletedTask;
            }
        };
    });
    //注入SignalR服务
    builder.Services.AddSignalR();
    //配置跨域交互
    string[] urls = new[] { "http://localhost:3000" };
    builder.Services.AddCors(options =>
        options.AddDefaultPolicy(builder => builder.WithOrigins(urls)
            .AllowAnyMethod().AllowAnyHeader().AllowCredentials())
    );
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    app.UseCors();
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();
    //添加我们的hub类
    app.MapHub<ChatRoomHub>("/ChatRoomHub");
    app.MapControllers();
    
    app.Run();
    

(基本使用结束)

SignalR的身份认证(我做的时候不方便连数据库,所以自己写usermanerg类代替)
  • 安装Microsoft.AspNetCore.Authentication.JwtBearer(jwt)、Microsoft.AspNetCore.Identity.EntityFrameworkCore
  • 在hub类加上[Authorize]
  • 在appsettings.json配置jwt密钥、超时,添加jwoption类
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "JWT": {
        "SigningKey": "fasdfad&9045dafz222#fadpio@02328hdahihiasuighcaui",
        "ExpireSeconds": "86400"
      }   
    }
    
    namespace signalRTest
    {
        public class JWTOption
        {
            public string SigningKey {  get; set; }
            public int ExpireSeconds {  get; set; }
        }
    }
    
  • 我的usermanager类
    using System.Runtime.CompilerServices;
    
    namespace signalRTest
    {
        public class UserManeger
        {
            public static User? FindByName(string name)
            {
                return GetAll().SingleOrDefault(u => u.name == name);
            }
            public static User? FindById(int id) 
            {
                return GetAll().SingleOrDefault(u => u.id == id);
            }
            public static List<User> GetAll()
            {
                List<User> users = new List<User>();
                users.Add(new User(1, "yzk", "123456"));
                users.Add(new User(2, "siruis", "123456"));
                users.Add(new User(3, "jack", "123456"));
                users.Add(new User(4, "lili", "123456"));
                return users;
            }
            public record class User(int id, string name, string password) { }
    
        }
    }
    
  • 前端代码:
    <template>
    	<fieldset>
    		<legend>登录</legend>
    		<div>
    			<div>
    				用户名:<input type="text" v-model="state.loginData.UserName" />
    			</div>
    			<div>
    				密 码:<input type="password" v-model="state.loginData.PassWord" />
    			</div>
    			<div>
    				<input type="button" value="登录" v-on:click="loginClick" />
    			</div>
    		</div>
    	</fieldset>
    	公屏:<input type="text" v-model="state.userMessage" v-on:keypress="txtMsgOnkeypress" />
    	<div>
    		私聊给:<input type="text" v-model="state.privateMsg.destUserName" />
    		信息:<input type="text" v-model="state.privateMsg.message" v-on:keypress="txtPrivareMsgOnkeypress" />
    	</div>
    	<div>
    		<ul>
    			<li v-for="(msg,index) in state.messages" :key="index">{{msg}}</li>
    		</ul>
    	</div>
    </template>
    <script>
    	import {
    		reactive,
    		onMounted
    	} from 'vue';
    	import * as signalR from '@microsoft/signalr';
    	import axios from 'axios';
    	let connection;
    	export default {
    		name: 'SignalR',
    		setup() {
    			const state = reactive({
    				userMessage: "",
    				messages: [],
    				accessTocken:"",
    				loginData:{UserName:"",PassWord:""},
    				privateMsg:{destUserName:"",message:""}
    			});
    			 const startConn = async function () {
    			                const transport = signalR.HttpTransportType.WebSockets;
    			                const options = { skipNegotiation: true, transport: transport };
    			                options.accessTokenFactory = () => state.accessTocken;
    			                connection = new signalR.HubConnectionBuilder()
    			                    .withUrl('https://localhost:7229/ChatRoomHub', options)
    			                    .withAutomaticReconnect().build();
    			                try {
    			                    await connection.start();
    			                } catch (err) {
    			                    alert(err);
    			                    return;
    			                }
    			                connection.on('ReceivePublicMessage', msg => {
    			                    state.messages.push(msg);
    			                });
    			                connection.on('ReceivePriveteMessage', (srcUser,time,msg) => {
    			                    state.messages.push(srcUser+"在"+time+"发来私信:"+msg);
    			                });
    			                connection.on('UserAdded', userName => {
    			                    state.messages.push("系统消息:欢迎" + userName+"加入我们!");
    			                });
    			                alert("登陆成功可以聊天了");
    			            };
    			const loginClick = async function(){
    				const req=await axios.post('https://localhost:7229/api/UserLong/Longin',state.loginData);
    				state.accessTocken=req.data;
    				startConn();
    			};
    			const txtMsgOnkeypress = async function(e) {
    				if (e.keyCode != 13) return;
    				await connection.invoke("SendPublicMessage", state.userMessage);
    				state.userMessage = "";
    			};
    			const txtPrivareMsgOnkeypress =async function (e) {
    				if(e.keyCode!=13)return;
    				var destUserName=state.privateMsg.destUserName;
    				var msg=state.privateMsg.message;
    				try{
    					const ret=await connection.invoke("SendPrivateMessage",destUserName,msg);
    					
    					if(ret!="ok"){alert(ret)};
    				}catch(err){
    					alert(err);
    					return;
    				}
    				state.privateMsg.message=""
    			}
    			return {
    				state,
    				txtMsgOnkeypress,
    				loginClick,
    				txtPrivareMsgOnkeypress
    			};
    		},
    	}
    </script>

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值