mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-20 00:06:45 +08:00
web socket添加token验证能力(使用token+创建时设置验证回调);flowEnv添加WebSocket处理消息能力,下一步将开发远程登录登录工具。
This commit is contained in:
@@ -80,6 +80,12 @@ namespace Serein.Library.Api
|
||||
/// <param name="eventArgs"></param>
|
||||
public delegate void NodeLocatedHandler(NodeLocatedEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// 节点移动了(远程插件)
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
public delegate void NodeMovedHandler(NodeMovedEventArgs eventArgs);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 环境事件签名
|
||||
@@ -381,7 +387,9 @@ namespace Serein.Library.Api
|
||||
public object Instance { get; private set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 节点需要定位
|
||||
/// </summary>
|
||||
public class NodeLocatedEventArgs : FlowEventArgs
|
||||
{
|
||||
public NodeLocatedEventArgs(string nodeGuid)
|
||||
@@ -391,6 +399,31 @@ namespace Serein.Library.Api
|
||||
public string NodeGuid { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点移动了
|
||||
/// </summary>
|
||||
public class NodeMovedEventArgs : FlowEventArgs
|
||||
{
|
||||
public NodeMovedEventArgs(string nodeGuid, double x, double y)
|
||||
{
|
||||
this.NodeGuid = nodeGuid;
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
/// <summary>
|
||||
/// 节点唯一标识
|
||||
/// </summary>
|
||||
public string NodeGuid { get; private set; }
|
||||
/// <summary>
|
||||
/// 画布上的x坐标
|
||||
/// </summary>
|
||||
public double X { get; private set; }
|
||||
/// <summary>
|
||||
/// 画布上的y坐标
|
||||
/// </summary>
|
||||
public double Y { get; private set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -492,11 +525,15 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
event IOCMembersChangedHandler OnIOCMembersChanged;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 节点需要定位
|
||||
/// </summary>
|
||||
event NodeLocatedHandler OnNodeLocate;
|
||||
event NodeLocatedHandler OnNodeLocated;
|
||||
|
||||
/// <summary>
|
||||
/// 节点移动了(远程插件)
|
||||
/// </summary>
|
||||
event NodeMovedHandler OnNodeMoved;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -520,6 +557,15 @@ namespace Serein.Library.Api
|
||||
//bool TryGetNodeData(string methodName, out NodeData node);
|
||||
|
||||
#region 环境基础接口
|
||||
/// <summary>
|
||||
/// 启动远程服务
|
||||
/// </summary>
|
||||
Task StartRemoteServerAsync(int port = 7525);
|
||||
/// <summary>
|
||||
/// 停止远程服务
|
||||
/// </summary>
|
||||
void StopRemoteServer();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存当前项目
|
||||
@@ -532,7 +578,6 @@ namespace Serein.Library.Api
|
||||
/// <param name="projectFile"></param>
|
||||
/// <param name="filePath"></param>
|
||||
void LoadProject(SereinProjectData projectFile, string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// 加载远程项目
|
||||
/// </summary>
|
||||
@@ -556,8 +601,7 @@ namespace Serein.Library.Api
|
||||
/// 清理加载的DLL(待更改)
|
||||
/// </summary>
|
||||
void ClearAll();
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始运行
|
||||
/// </summary>
|
||||
@@ -567,18 +611,27 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task StartFlowInSelectNodeAsync(string startNodeGuid);
|
||||
Task StartAsyncInSelectNode(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行
|
||||
/// </summary>
|
||||
void Exit();
|
||||
void ExitFlow();
|
||||
|
||||
/// <summary>
|
||||
/// 移动了某个节点(远程插件使用)
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
void MoveNode(string nodeGuid,double x, double y);
|
||||
|
||||
/// <summary>
|
||||
/// 设置流程起点节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
void SetStartNode(string nodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 在两个节点之间创建连接关系
|
||||
/// </summary>
|
||||
@@ -586,6 +639,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType);
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点/区域/基础控件
|
||||
/// </summary>
|
||||
@@ -601,6 +655,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="toNodeGuid">目标节点</param>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType);
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点/区域/基础控件
|
||||
/// </summary>
|
||||
@@ -612,6 +667,7 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
void ActivateFlipflopNode(string nodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 终结一个全局触发器,在它触发后将不会再次监听消息(表现为已经启动的触发器至少会再次处理一次消息,后面版本再修正这个非预期行为)
|
||||
/// </summary>
|
||||
|
||||
@@ -129,7 +129,15 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
foreach ((var model, var method) in methods)
|
||||
{
|
||||
Console.WriteLine($"theme value : {model.ThemeValue}");
|
||||
handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking);
|
||||
try
|
||||
{
|
||||
handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Console.WriteLine($"error in add method: {method.Name}{Environment.NewLine}{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -147,7 +155,6 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
{
|
||||
foreach (var module in MyHandleModuleDict.Values)
|
||||
{
|
||||
|
||||
module.HandleSocketMsg(RecoverAsync, json);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Network.WebSocketCommunication.Handle;
|
||||
using Serein.Library.Web;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -13,35 +14,58 @@ using System.Threading.Tasks;
|
||||
namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket客户端
|
||||
/// </summary>
|
||||
[AutoRegister]
|
||||
public class WebSocketClient
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket客户端
|
||||
/// </summary>
|
||||
public WebSocketClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 消息处理
|
||||
/// </summary>
|
||||
public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper();
|
||||
|
||||
private ClientWebSocket _client = new ClientWebSocket();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接到指定WebSocket Server服务
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ConnectAsync(string uri)
|
||||
{
|
||||
await _client.ConnectAsync(new Uri(uri), CancellationToken.None);
|
||||
await ReceiveAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task SendAsync(WebSocket webSocket,string message)
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="webSocket"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SendAsync(string message)
|
||||
{
|
||||
var buffer = Encoding.UTF8.GetBytes(message);
|
||||
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
await _client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始处理消息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task ReceiveAsync()
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
|
||||
while (_client.State == WebSocketState.Open)
|
||||
{
|
||||
try
|
||||
@@ -54,8 +78,7 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
else
|
||||
{
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
|
||||
|
||||
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
|
||||
Debug.WriteLine($"Received: {message}");
|
||||
}
|
||||
}
|
||||
@@ -67,9 +90,6 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Network.WebSocketCommunication.Handle;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -14,6 +15,84 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket JSON 消息授权管理
|
||||
/// </summary>
|
||||
public class WebSocketAuthorizedHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket JSON 消息授权管理
|
||||
/// </summary>
|
||||
public WebSocketAuthorizedHelper(string addresPort,string token, Func<dynamic, Task<bool>> inspectionAuthorizedFunc)
|
||||
{
|
||||
this.AddresPort = addresPort;
|
||||
this.TokenKey = token;
|
||||
this.InspectionAuthorizedFunc = inspectionAuthorizedFunc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户端地址
|
||||
/// </summary>
|
||||
public string AddresPort { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经鉴权
|
||||
/// </summary>
|
||||
public bool IsAuthorized { get => isAuthorized; } //set => isAuthorized = value;
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经鉴权
|
||||
/// </summary>
|
||||
private bool isAuthorized;
|
||||
|
||||
/// <summary>
|
||||
/// 授权字段
|
||||
/// </summary>
|
||||
private readonly string TokenKey;
|
||||
|
||||
/// <summary>
|
||||
/// 处理消息授权事件
|
||||
/// </summary>
|
||||
private readonly Func<dynamic, Task<bool>> InspectionAuthorizedFunc;
|
||||
|
||||
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
||||
|
||||
/// <summary>
|
||||
/// 处理消息授权
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public async Task HandleAuthorized(string message)
|
||||
{
|
||||
if(!isAuthorized && semaphoreSlim is null) // 需要重新授权
|
||||
{
|
||||
semaphoreSlim = new SemaphoreSlim(1);
|
||||
}
|
||||
await semaphoreSlim.WaitAsync(1);
|
||||
if(isAuthorized) // 授权通过,无须再次检查授权
|
||||
{
|
||||
return;
|
||||
}
|
||||
JObject json = JObject.Parse(message);
|
||||
if(json.TryGetValue(TokenKey,out var token))
|
||||
{
|
||||
// 交给之前定义的授权方法进行判断
|
||||
isAuthorized = await InspectionAuthorizedFunc?.Invoke(token);
|
||||
if (isAuthorized)
|
||||
{
|
||||
// 授权通过,释放资源
|
||||
semaphoreSlim.Release();
|
||||
semaphoreSlim.Dispose();
|
||||
semaphoreSlim = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isAuthorized = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket服务类
|
||||
@@ -28,6 +107,36 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
|
||||
private HttpListener listener;
|
||||
|
||||
/// <summary>
|
||||
/// 创建无须授权验证的WebSocket服务端
|
||||
/// </summary>
|
||||
public WebSocketServer()
|
||||
{
|
||||
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
|
||||
this.InspectionAuthorizedFunc = (tokenObj) => Task.FromResult(true);
|
||||
this.IsNeedInspectionAuthorized = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建需要授权验证的WebSocket服务端
|
||||
/// </summary>
|
||||
/// <param name="tokenKey">token 字段</param>
|
||||
/// <param name="inspectionAuthorizedFunc">验证token的方法</param>
|
||||
public WebSocketServer(string tokenKey, Func<dynamic, Task<bool>> inspectionAuthorizedFunc)
|
||||
{
|
||||
this.TokenKey = tokenKey;
|
||||
this.AuthorizedClients = new ConcurrentDictionary<string, WebSocketAuthorizedHelper>();
|
||||
this.InspectionAuthorizedFunc = inspectionAuthorizedFunc;
|
||||
this.IsNeedInspectionAuthorized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 授权
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, WebSocketAuthorizedHelper> AuthorizedClients;
|
||||
private readonly string TokenKey;
|
||||
private readonly Func<dynamic, Task<bool>> InspectionAuthorizedFunc;
|
||||
private bool IsNeedInspectionAuthorized = false;
|
||||
/// <summary>
|
||||
/// 进行监听服务
|
||||
/// </summary>
|
||||
@@ -37,6 +146,7 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add(url);
|
||||
await Console.Out.WriteLineAsync($"WebSocket消息处理已启动[{url}]");
|
||||
try
|
||||
{
|
||||
listener.Start();
|
||||
@@ -46,7 +156,6 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -56,11 +165,20 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
string clientPoint = context.Request.RemoteEndPoint?.ToString();
|
||||
|
||||
await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}");
|
||||
|
||||
if (context.Request.IsWebSocketRequest)
|
||||
{
|
||||
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
|
||||
WebSocketAuthorizedHelper authorizedHelper = null;
|
||||
if (IsNeedInspectionAuthorized)
|
||||
{
|
||||
if (AuthorizedClients.TryAdd(clientPoint, new WebSocketAuthorizedHelper(clientPoint, TokenKey, InspectionAuthorizedFunc)))
|
||||
{
|
||||
AuthorizedClients.TryGetValue(clientPoint, out authorizedHelper);
|
||||
}
|
||||
}
|
||||
|
||||
_ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息
|
||||
var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接
|
||||
_ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -79,12 +197,21 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
listener?.Stop();
|
||||
}
|
||||
|
||||
private async Task HandleWebSocketAsync(WebSocket webSocket)
|
||||
private async Task HandleWebSocketAsync(WebSocket webSocket, WebSocketAuthorizedHelper authorizedHelper)
|
||||
{
|
||||
Func<string,Task> SendAsync = async (text) =>
|
||||
// 需要授权,却没有成功创建授权类,关闭连接
|
||||
if (IsNeedInspectionAuthorized && authorizedHelper is null)
|
||||
{
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Func<string, Task> SendAsync = async (text) =>
|
||||
{
|
||||
await WebSocketServer.SendAsync(webSocket, text);
|
||||
};
|
||||
|
||||
var buffer = new byte[1024];
|
||||
while (webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
@@ -93,21 +220,45 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
SendAsync = null;
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||
if (IsNeedInspectionAuthorized)
|
||||
{
|
||||
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
|
||||
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message);
|
||||
|
||||
//foreach (var item in HandldHelpers)
|
||||
//{
|
||||
// await item.HandleSocketMsg(webSocket, message);
|
||||
//}
|
||||
//Console.WriteLine($"Received: {message}");
|
||||
//var echoMessage = Encoding.UTF8.GetBytes(message);
|
||||
//await webSocket.SendAsync(new ArraySegment<byte>(echoMessage, 0, echoMessage.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count); // 序列为文本
|
||||
if(!IsNeedInspectionAuthorized)
|
||||
{
|
||||
// 无须授权
|
||||
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 需要授权
|
||||
if (!authorizedHelper.IsAuthorized)
|
||||
{
|
||||
// 该连接尚未验证授权,尝试检测授权
|
||||
_ = SendAsync("正在授权");
|
||||
await authorizedHelper.HandleAuthorized(message);
|
||||
}
|
||||
|
||||
|
||||
if (authorizedHelper.IsAuthorized)
|
||||
{
|
||||
// 该连接通过了验证
|
||||
_ = SendAsync("授权成功");
|
||||
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = SendAsync("授权失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Serein.Library.Attributes
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 注册顺序
|
||||
/// </summary>
|
||||
public enum RegisterSequence
|
||||
{ /// <summary>
|
||||
/// 不自动初始化
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.13</Version>
|
||||
<TargetFrameworks>net8.0;net462</TargetFrameworks>
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
|
||||
Reference in New Issue
Block a user