开启单设备登陆情况下,单用户可以在PC、APP端同时登陆连接signlar,不被强制下线并可以接收消息

This commit is contained in:
NG-GGBond 2024-08-09 17:44:52 +08:00
parent 97043f00fd
commit 4368c5510f
8 changed files with 69 additions and 21 deletions

View File

@ -99,7 +99,7 @@ public class AppAuthService : IDynamicApiController, ITransient
// 登录成功则清空密码错误次数
_sysCacheService.Remove(keyPasswordErrorTimes);
return await CreateToken(user);
return await CreateToken(user, input.LoginMode);
}
/// <summary>
@ -148,19 +148,23 @@ public class AppAuthService : IDynamicApiController, ITransient
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter().FirstAsync(u => u.Phone.Equals(input.Phone));
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
return await CreateToken(user);
return await CreateToken(user, input.LoginMode);
}
/// <summary>
/// 生成Token令牌 🔖
/// </summary>
/// <param name="user"></param>
/// <param name="loginMode"></param>
/// <returns></returns>
[NonAction]
public virtual async Task<LoginOutput> CreateToken(SysUser user)
public virtual async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode)
{
// 兼容处理前端未传递loginMode,导致收不到消息
if (loginMode == 0) loginMode = LoginModeEnum.PC;
// 单用户登录
await _sysOnlineUserService.SingleLogin(user.Id);
await _sysOnlineUserService.SingleLogin(user.Id, loginMode);
// 生成Token令牌
var tokenExpire = await _sysConfigService.GetTokenExpire();
@ -174,6 +178,7 @@ public class AppAuthService : IDynamicApiController, ITransient
{ AppClaimConst.OrgId, user.OrgId },
{ AppClaimConst.OrgName, user.SysOrg?.Name },
{ AppClaimConst.OrgType, user.SysOrg?.Type },
{ ClaimConst.LoginMode, loginMode },
}, tokenExpire);
// 生成刷新Token令牌

View File

@ -65,4 +65,10 @@ public partial class SysOnlineUser : EntityTenantId
[SugarColumn(ColumnDescription = "操作系统", Length = 128)]
[MaxLength(128)]
public string? Os { get; set; }
/// <summary>
/// 登陆模式
/// </summary>
[SugarColumn(ColumnDescription = "登陆模式")]
public LoginModeEnum LoginMode { get; set; }
}

View File

@ -47,6 +47,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
var account = httpContext.User.FindFirst(ClaimConst.Account)?.Value;
var realName = httpContext.User.FindFirst(ClaimConst.RealName)?.Value;
var tenantId = (httpContext.User.FindFirst(ClaimConst.TenantId)?.Value).ToLong();
var loginMode = (LoginModeEnum)(httpContext.User.FindFirst(ClaimConst.LoginMode)?.Value).ToInt();
var user = new SysOnlineUser
{
@ -59,13 +60,14 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
Browser = httpContext.GetClientBrowser(),
Os = httpContext.GetClientOs(),
TenantId = tenantId,
LoginMode = loginMode,
};
await _sysOnlineUerRep.InsertAsync(user);
// 是否开启单用户登录
if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysSingleLogin))
{
_sysCacheService.Set(CacheConst.KeyUserOnline + user.UserId, user);
_sysCacheService.Set(CacheConst.KeyUserOnline + user.UserId + loginMode, user);
}
else
{

View File

@ -34,6 +34,11 @@ public class LoginInput
/// 验证码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 登陆模式
/// </summary>
public LoginModeEnum LoginMode { get; set; }
}
public class LoginPhoneInput
@ -52,4 +57,9 @@ public class LoginPhoneInput
/// <example>123456</example>
[Required(ErrorMessage = "验证码不能为空"), MinLength(4, ErrorMessage = "验证码不能少于4个字符")]
public string Code { get; set; }
/// <summary>
/// 登陆模式
/// </summary>
public LoginModeEnum LoginMode { get; set; }
}

View File

@ -123,7 +123,7 @@ public class SysAuthService : IDynamicApiController, ITransient
// 登录成功则清空密码错误次数
_sysCacheService.Remove(keyPasswordErrorTimes);
return await CreateToken(user);
return await CreateToken(user, input.LoginMode);
}
/// <summary>
@ -202,19 +202,23 @@ public class SysAuthService : IDynamicApiController, ITransient
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter().FirstAsync(u => u.Phone.Equals(input.Phone));
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
return await CreateToken(user);
return await CreateToken(user, input.LoginMode);
}
/// <summary>
/// 生成Token令牌 🔖
/// </summary>
/// <param name="user"></param>
/// <param name="loginMode"></param>
/// <returns></returns>
[NonAction]
internal async Task<LoginOutput> CreateToken(SysUser user)
internal async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode)
{
// 兼容处理前端未传递loginMode,导致收不到消息
if (loginMode == 0) loginMode = LoginModeEnum.PC;
// 单用户登录
await _sysOnlineUserService.SingleLogin(user.Id);
await _sysOnlineUserService.SingleLogin(user.Id, loginMode);
// 生成Token令牌
var tokenExpire = await _sysConfigService.GetTokenExpire();
@ -228,6 +232,7 @@ public class SysAuthService : IDynamicApiController, ITransient
{ ClaimConst.OrgId, user.OrgId },
{ ClaimConst.OrgName, user.SysOrg?.Name },
{ ClaimConst.OrgType, user.SysOrg?.Type },
{ ClaimConst.LoginMode, loginMode },
}, tokenExpire);
// 生成刷新Token令牌

View File

@ -50,9 +50,14 @@ public class SysMessageService : IDynamicApiController, ITransient
// 是否开启单用户登录
if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysSingleLogin))
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey);
if (user == null) return;
await _chatHubContext.Clients.AllExcept(user.ConnectionId).ReceiveMessage(input);
var values = Enum.GetValues(typeof(LoginModeEnum)).Cast<LoginModeEnum>().ToList();
foreach (var value in values)
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey + value);
if (user == null) continue;
await _chatHubContext.Clients.AllExcept(user.ConnectionId).ReceiveMessage(input);
}
}
else
{
@ -78,9 +83,14 @@ public class SysMessageService : IDynamicApiController, ITransient
// 是否开启单用户登录
if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysSingleLogin))
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey);
if (user == null) return;
await _chatHubContext.Clients.Client(user.ConnectionId).ReceiveMessage(input);
var values = Enum.GetValues(typeof(LoginModeEnum)).Cast<LoginModeEnum>().ToList();
foreach (var value in values)
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey + value);
if (user == null) continue;
await _chatHubContext.Clients.Client(user.ConnectionId).ReceiveMessage(input);
}
}
else
{
@ -109,8 +119,13 @@ public class SysMessageService : IDynamicApiController, ITransient
// 是否开启单用户登录
if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysSingleLogin))
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey);
if (user != null) userList.Add(user.ConnectionId);
var values = Enum.GetValues(typeof(LoginModeEnum)).Cast<LoginModeEnum>().ToList();
foreach (var value in values)
{
var user = _sysCacheService.Get<SysOnlineUser>(cacheKey + value);
if (user != null) userList.Add(user.ConnectionId);
}
}
else
{

View File

@ -109,8 +109,8 @@ public class SysOAuthService : IDynamicApiController, ITransient
wechatUser = await _sysOAuthUserRep.AsQueryable().Includes(u => u.SysUser).ClearFilter().FirstAsync(u => u.OpenId == openIdClaim.Value);
}
// 构建Token令牌
var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser);
// 构建Token令牌 TODO 这里先默认所有回调登陆的都是PC
var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser, LoginModeEnum.PC);
return new RedirectResult($"{redirectUrl}/#/login?token={token.AccessToken}");
}

View File

@ -74,16 +74,21 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
/// <summary>
/// 单用户登录
/// </summary>
/// <param name="userId"></param>
/// <param name="loginMode"></param>
/// <returns></returns>
[NonAction]
public async Task SingleLogin(long userId)
public async Task SingleLogin(long userId, LoginModeEnum loginMode)
{
if (await _sysConfigService.GetConfigValue<bool>(ConfigConst.SysSingleLogin))
{
var users = await _sysOnlineUerRep.GetListAsync(u => u.UserId == userId);
foreach (var user in users)
{
await ForceOffline(user);
if (loginMode == user.LoginMode)
{
await ForceOffline(user);
}
}
}
}