新增单点登录功能、在线时长

This commit is contained in:
不做码农 2023-08-27 20:51:47 +08:00
parent 55733f07e8
commit 839e400ed1
12 changed files with 186 additions and 59 deletions

14
Infrastructure/Log.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
namespace Infrastructure
{
public class Log
{
public static void WriteLine(ConsoleColor color = ConsoleColor.Black, string msg = "")
{
Console.ForegroundColor = color;
Console.WriteLine($"{DateTime.Now} {msg}");
Console.ResetColor();
}
}
}

View File

@ -1,6 +1,4 @@

using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace Infrastructure
{
@ -9,6 +7,10 @@ namespace Infrastructure
/// </summary>
public class OptionsSetting
{
/// <summary>
/// 是否单点登录
/// </summary>
public bool SingleLogin { get; set; }
/// <summary>
/// 是否演示模式
/// </summary>

View File

@ -1,8 +1,10 @@
using Infrastructure;
using Infrastructure.Attribute;
using Infrastructure.Attribute;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection;
namespace ZR.Admin.WebApi.Extensions
namespace Infrastructure
{
/// <summary>
/// App服务注册

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
@ -8,7 +9,7 @@ using System.Text;
using System.Text.RegularExpressions;
using UAParser;
namespace Infrastructure.Extensions
namespace Infrastructure.WebExtensins
{
/// <summary>
/// HttpContext扩展类

View File

@ -1,5 +1,4 @@
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

View File

@ -2,10 +2,7 @@
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using SqlSugar;
using System.Diagnostics;
using UAParser;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Framework;
using ZR.Model.System;
@ -31,7 +28,7 @@ namespace ZR.Admin.WebApi.Controllers.System
private readonly ICaptcha SecurityCodeHelper;
private readonly ISysConfigService sysConfigService;
private readonly ISysRoleService roleService;
private readonly OptionsSetting jwtSettings;
private readonly OptionsSetting optionSettings;
public SysLoginController(
IHttpContextAccessor contextAccessor,
@ -42,7 +39,7 @@ namespace ZR.Admin.WebApi.Controllers.System
ISysConfigService configService,
ISysRoleService sysRoleService,
ICaptcha captcha,
IOptions<OptionsSetting> jwtSettings)
IOptions<OptionsSetting> optionSettings)
{
httpContextAccessor = contextAccessor;
SecurityCodeHelper = captcha;
@ -52,7 +49,7 @@ namespace ZR.Admin.WebApi.Controllers.System
this.permissionService = permissionService;
this.sysConfigService = configService;
roleService = sysRoleService;
this.jwtSettings = jwtSettings.Value;
this.optionSettings = optionSettings.Value;
}
@ -82,7 +79,6 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ResultCode.LOGIN_ERROR, $"你的账号已被锁,剩余{Math.Round(ts.TotalMinutes, 0)}分钟");
}
var user = sysLoginService.Login(loginBody, RecordLogInfo(httpContextAccessor.HttpContext));
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
@ -91,7 +87,7 @@ namespace ZR.Admin.WebApi.Controllers.System
LoginUser loginUser = new(user, roles, permissions);
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), jwtSettings.JwtSettings));
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), optionSettings.JwtSettings));
}
/// <summary>
@ -131,6 +127,9 @@ namespace ZR.Admin.WebApi.Controllers.System
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
//LoginUser loginUser = new(user, roleService.SelectUserRoleListByUserId(user.UserId), permissions);
//var token = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), optionSettings.JwtSettings);
return SUCCESS(new { user, roles, permissions });
}
@ -300,5 +299,6 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ResultCode.FAIL, "二维码已失效");
}
#endregion
}
}

View File

@ -30,11 +30,11 @@ namespace ZR.Admin.WebApi.Controllers.monitor
[HttpGet("list")]
public IActionResult Index([FromQuery] PagerInfo parm)
{
var result = MessageHub.clientUsers
var result = MessageHub.onlineClients
.OrderByDescending(f => f.LoginTime)
.Skip(parm.PageNum - 1).Take(parm.PageSize);
return SUCCESS(new { result, totalNum = MessageHub.clientUsers.Count });
return SUCCESS(new { result, totalNum = MessageHub.onlineClients.Count });
}
/// <summary>

View File

@ -1,7 +1,6 @@
using JinianNet.JNTemplate;
using ZR.Common;
namespace ZR.Admin.WebApi.Extensions
namespace Infrastructure
{
public static class LogoExtension
{

View File

@ -6,3 +6,4 @@ global using Infrastructure.Enums;
global using Infrastructure.Model;
global using Mapster;
global using Infrastructure.Extensions;
global using Infrastructure.WebExtensins;

View File

@ -13,8 +13,9 @@ namespace ZR.Admin.WebApi.Hubs
public class MessageHub : Hub
{
//创建用户集合,用于存储所有链接的用户数据
public static readonly List<OnlineUsers> clientUsers = new();
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public static readonly List<OnlineUsers> onlineClients = new();
public static List<OnlineUsers> users = new();
//private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly ISysNoticeService SysNoticeService;
public MessageHub(ISysNoticeService noticeService)
@ -43,14 +44,13 @@ namespace ZR.Admin.WebApi.Hubs
ClientInfo clientInfo = HttpContextExtension.GetClientInfo(App.HttpContext);
string device = clientInfo.ToString();
var qs = HttpContextExtension.GetQueryString(App.HttpContext);
string qs = HttpContextExtension.GetQueryString(App.HttpContext);
string from = HttpUtility.ParseQueryString(qs).Get("from") ?? "web";
long userid = HttpContextExtension.GetUId(App.HttpContext);
string uuid = device + userid + ip;
var user = clientUsers.Any(u => u.ConnnectionId == Context.ConnectionId);
var user2 = clientUsers.Any(u => u.Uuid == uuid);
var user = onlineClients.Any(u => u.ConnnectionId == Context.ConnectionId);
var user2 = onlineClients.Any(u => u.Uuid == uuid);
//判断用户是否存在,否则添加集合!user2 && !user &&
if (!user2 && !user && Context.User.Identity.IsAuthenticated)
@ -61,14 +61,41 @@ namespace ZR.Admin.WebApi.Hubs
Uuid = uuid,
Platform = from
};
clientUsers.Add(onlineUser);
Console.WriteLine($"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{clientUsers.Count}个");
onlineClients.Add(onlineUser);
Log.WriteLine(msg: $"{DateTime.Now}{name},{Context.ConnectionId}连接服务端success当前已连接{onlineClients.Count}个");
//Clients.All.SendAsync("welcome", $"欢迎您:{name},当前时间:{DateTime.Now}");
Clients.Caller.SendAsync(HubsConstant.MoreNotice, SendNotice());
Clients.Caller.SendAsync(HubsConstant.ConnId, onlineUser.ConnnectionId);
}
OnlineUsers? userInfo = GetUserById(userid);
if (userInfo == null)
{
userInfo = new OnlineUsers() { Userid = userid, Name = name, LoginTime = DateTime.Now };
users.Add(userInfo);
}
else
{
if (userInfo.LoginTime <= Convert.ToDateTime(DateTime.Now.ToShortDateString()))
{
userInfo.LoginTime = DateTime.Now;
userInfo.TodayOnlineTime = 0;
}
var clientUser = onlineClients.Find(x => x.Userid == userid);
userInfo.TodayOnlineTime += clientUser?.OnlineTime ?? 0;
}
//给当前所有登录当前账号的用户下发登录时长
var connIds = onlineClients.Where(f => f.Userid == userid).ToList();
userInfo.ClientNum = connIds.Count;
Clients.Caller.SendAsync(HubsConstant.OnlineNum, clientUsers.Count);
Clients.Clients(connIds.Select(f => f.ConnnectionId)).SendAsync("onlineInfo", userInfo);
Log.WriteLine(ConsoleColor.Blue, msg: $"用户{name}已连接,今日已在线{userInfo?.TodayOnlineTime}分钟,当前已连接{onlineClients.Count}个");
//给所有用户更新在线人数
Clients.All.SendAsync(HubsConstant.OnlineNum, new
{
num = onlineClients.Count,
onlineClients
});
return base.OnConnectedAsync();
}
@ -78,14 +105,25 @@ namespace ZR.Admin.WebApi.Hubs
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception? exception)
{
var user = clientUsers.Where(p => p.ConnnectionId == Context.ConnectionId).FirstOrDefault();
//判断用户是否存在,否则添加集合
var user = onlineClients.Where(p => p.ConnnectionId == Context.ConnectionId).FirstOrDefault();
if (user != null)
{
clientUsers.Remove(user);
Clients.All.SendAsync(HubsConstant.OnlineNum, clientUsers.Count);
onlineClients.Remove(user);
//给所有用户更新在线人数
Clients.All.SendAsync(HubsConstant.OnlineNum, new
{
num = onlineClients.Count,
onlineClients,
leaveUser = user
});
Console.WriteLine($"用户{user?.Name}离开了,当前已连接{clientUsers.Count}个");
//累计用户时长
OnlineUsers? userInfo = GetUserById(user.Userid);
if (userInfo != null)
{
userInfo.TodayOnlineTime += user?.OnlineTime ?? 0;
}
Log.WriteLine(ConsoleColor.Green, msg: $"用户{user?.Name}离开了,已在线{userInfo?.TodayOnlineTime}分,当前已连接{onlineClients.Count}个");
}
return base.OnDisconnectedAsync(exception);
}
@ -95,20 +133,54 @@ namespace ZR.Admin.WebApi.Hubs
/// <summary>
/// 发送信息
/// </summary>
/// <param name="connectId"></param>
/// <param name="userName"></param>
/// <param name="toConnectId">对方链接id</param>
/// <param name="toUserId"></param>
/// <param name="message"></param>
/// <returns></returns>
[HubMethodName("SendMessage")]
public async Task SendMessage(string connectId, string userName, string message)
[HubMethodName("sendMessage")]
public async Task SendMessage(string toConnectId, long toUserId, string message)
{
Console.WriteLine($"{connectId},message={message}");
var userName = HttpContextExtension.GetName(App.HttpContext);
long userid = HttpContextExtension.GetUId(App.HttpContext);
var toUserList = onlineClients.Where(p => p.Userid == toUserId);
var toUserInfo = toUserList.FirstOrDefault();
IList<string> sendToUser = toUserList.Select(x => x.ConnnectionId).ToList();
sendToUser.Add(GetConnectId());
if (toUserInfo != null)
{
await Clients.Clients(sendToUser)
.SendAsync("receiveChat", new
{
msgType = 0,//文本
chatid = Guid.NewGuid().ToString(),
userName,
userid,
toUserName = toUserInfo.Name,
toUserid = toUserInfo.Userid,
message,
chatTime = DateTime.Now
});
}
else
{
//TODO 存储离线消息
Console.WriteLine($"{toUserId}不在线");
}
await Clients.Client(connectId).SendAsync("receiveChat", new { userName, message });
Console.WriteLine($"用户{userName}对{toConnectId}-{toUserId}说:{message}");
}
private OnlineUsers GetUserByConnId(string connId)
{
return onlineClients.Where(p => p.ConnnectionId == connId).FirstOrDefault();
}
private static OnlineUsers? GetUserById(long userid)
{
return users.Where(f => f.Userid == userid).FirstOrDefault();
}
/// <summary>
/// 获取链接id
/// 移动端使用获取链接id
/// </summary>
/// <returns></returns>
[HubMethodName("getConnId")]
@ -116,5 +188,22 @@ namespace ZR.Admin.WebApi.Hubs
{
return Context.ConnectionId;
}
/// <summary>
/// 退出其他设备登录
/// </summary>
/// <returns></returns>
[HubMethodName("logOut")]
public async Task LogOut()
{
var singleLogin = AppSettings.Get<bool>("singleLogin");
long userid = HttpContextExtension.GetUId(App.HttpContext);
if (singleLogin)
{
var onlineUsers = onlineClients.Where(p => p.ConnnectionId != Context.ConnectionId && p.Userid == userid);
await Clients.Clients(onlineUsers.Select(x => x.ConnnectionId))
.SendAsync("logOut");
}
}
}
}

View File

@ -9,10 +9,10 @@
/// <summary>
/// 用户id
/// </summary>
public long? Userid { get; set; }
public long Userid { get; set; }
public string Name { get; set; }
public DateTime LoginTime { get; set; }
public string UserIP { get; set; }
public string? UserIP { get; set; }
/// <summary>
/// 登录地点
/// </summary>
@ -21,17 +21,41 @@
/// <summary>
/// 判断用户唯一
/// </summary>
public string? Uuid{ get; set; }
public string? Uuid { get; set; }
/// <summary>
/// 浏览器
/// </summary>
public string Browser { get; set; }
public string? Browser { get; set; }
/// <summary>
/// 平台
/// </summary>
public string Platform { get; set; } = string.Empty;
public OnlineUsers(string clientid, string name, long? userid, string userip, string browser)
public string? Platform { get; set; } = string.Empty;
/// <summary>
/// 在线时长
/// </summary>
public double OnlineTime
{
get
{
var ts = DateTime.Now - LoginTime;
return Math.Round(ts.TotalMinutes, 2);
}
}
/// <summary>
/// 今日在线时长
/// </summary>
public double TodayOnlineTime { get; set; }
/// <summary>
/// 在线设备数
/// </summary>
public int ClientNum { get; set; }
/// <summary>
///
/// </summary>
public OnlineUsers()
{
}
public OnlineUsers(string clientid, string name, long userid, string userip, string browser)
{
ConnnectionId = clientid;
Name = name;

View File

@ -32,9 +32,10 @@
"Expire": 1440 //jwt
},
"InjectClass": [ "ZR.Repository", "ZR.Service", "ZR.Tasks" ], //
"ShowDbLog": true,//db
"ShowDbLog": true, //db
"InitDb": false, //db
"DemoMode": false, //
"SingleLogin": false,//
"Upload": {
"uploadUrl": "http://localhost:8888", //访
"localSavePath": "", // wwwroot
@ -128,12 +129,7 @@
"Period": "3s",
"Limit": 1
}
],
"IpRateLimitPolicies": {
//ip
"IpRules": [
]
}
},
//
"CaptchaOptions": {