using ATS.NonCustodial.Application.Base;
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth;
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Input;
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Output;
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.User;
using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input;
using ATS.NonCustodial.Domain.Entities.Admins;
using ATS.NonCustodial.Domain.Entities.Business;
using ATS.NonCustodial.Domain.Entities.Business.EarlyWarning;
using ATS.NonCustodial.Domain.Shared.Enums;
using ATS.NonCustodial.Domain.Shared.OrmRepositories.Basic.EfCore;
using ATS.NonCustodial.DynamicApi;
using ATS.NonCustodial.DynamicApi.Attributes;
using ATS.NonCustodial.Shared.Captcha.Dto;
using ATS.NonCustodial.Shared.Common.Attributes;
using ATS.NonCustodial.Shared.Common.Auth;
using ATS.NonCustodial.Shared.Common.Constants;
using ATS.NonCustodial.Shared.Common.Dtos;
using ATS.NonCustodial.Shared.Common.Enums;
using ATS.NonCustodial.Shared.Common.UnifiedResults;
using ATS.NonCustodial.Shared.Configurations.Options;
using ATS.NonCustodial.Shared.Extensions;
using ATS.NonCustodial.Shared.Helpers;
using ATS.NonCustodial.Shared.Helpers.Core.Hash;
using ATS.NonCustodial.Shared.Helpers.Http;
using ATS.NonCustodial.Shared.Tools.Captcha;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using NPOI.Util;
using StackExchange.Profiling;
using System.Diagnostics;
using System.Security.Claims;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ATS.NonCustodial.Application.Impl.Admins
{
///
/// 认证授权服务
///
/// Author:mxg
/// CreatedTimed:2022-05-15 11:04 PM
[DynamicApi(Area = "admin")]
public class AuthService : AdminAppServiceBase, IAuthService, IDynamicApi
{
#region Identity
private readonly VarifyCodeConfiguration _adminUiOptions;
private readonly IEfRepository _appUnitRepository;
private readonly IEfRepository _appPermissionRepository;
private readonly IEfRepository _appUserRepository;
private readonly IEfRepository _appUserRoleRepository;
private readonly IEfRepository _appRolePermissionRepository;
private readonly IEfRepository _appViewRepository;
private readonly IEfRepository _appEarlyWarningRepository;
private readonly IEfRepository _appEarlyWarningRuleRepository;
private readonly ICaptchaTool _captchaTool;
private readonly IUserService _userService;
private readonly JwtConfiguration _jwtConfiguration;
public AuthService(
VarifyCodeConfiguration adminUiOptions,
IEfRepository appUserRepository,
IEfRepository appUnitRepository,
IEfRepository appEarlyWarningRepository,
IEfRepository appEarlyWarningRuleRepository,
IEfRepository appPermissionRepository,
IEfRepository appUserRoleRepository,
IEfRepository appRolePermissionRepository,
IEfRepository appViewRepository,
ICaptchaTool captchaTool,
IUserService userService,
JwtConfiguration jwtConfiguration
)
{
_appEarlyWarningRepository = appEarlyWarningRepository;
_appEarlyWarningRuleRepository = appEarlyWarningRuleRepository;
_adminUiOptions = adminUiOptions;
_appUserRepository = appUserRepository;
_appUnitRepository = appUnitRepository;
_appPermissionRepository = appPermissionRepository;
_captchaTool = captchaTool;
_appUserRoleRepository = appUserRoleRepository;
_appRolePermissionRepository = appRolePermissionRepository;
_appViewRepository = appViewRepository;
_userService = userService;
_jwtConfiguration = jwtConfiguration;
}
#endregion Identity
///
/// 查询密钥
///
///
[HttpGet]
[AllowAnonymous]
[NoOperationLog]
public async Task GetPasswordEncryptKeyAsync()
{
//写入Redis
var guid = Guid.NewGuid().ToString("N");
var key = string.Format(CacheKey.passWordEncryptKey, guid);
var encyptKey = StringHelper.GenerateRandom(8);
await Cache.SetAsync(key, encyptKey, TimeSpan.FromMinutes(5));
var data = new { key = guid, encyptKey };
return ResultOutput.Ok(data);
}
///
/// 查询单位树
///
///
[HttpGet]
[AllowAnonymous]
[NoOperationLog]
public async Task GetUnitTreeAsync()
{
var rtnlist = new List();//五条件返回结果
//监管机构
var express = _appUnitRepository.AsQueryable(false, true).Where(q => q.Stat == 0);
if (express.Count() == 0) return ResultOutput.Ok(rtnlist);
//单位集合
var dwgllist = _appUnitRepository.AsQueryable(false, true).Where(q => q.Stat == 1).ToList();
foreach (var item in await express.Where(q => q.ParentUnitCode == null || q.ParentUnitCode == 0).ToArrayAsync())
{
rtnlist.Add(new
{
Id = item.Id,
ParentUnitCode = item.ParentUnitCode,
UnitCode = item.UnitCode,
NameEntity = item.NameEntity,
dw = true,
children = pidlist(express.Where(q => q.ParentUnitCode != null || q.ParentUnitCode != 0).ToList(), item.Id, dwgllist)
});
}
return ResultOutput.Ok(rtnlist);
}
[HttpGet]
[AllowAnonymous]
[NoOperationLog]
public async Task GetEarlyWarningAsync(int noticeCount)
{
var rtnlist = new List();//五条件返回结果
//监管机构
//var express = _appEarlyWarningRepository.ExecuteSqlRawAsync("");
//var express = _appEarlyWarningRuleRepository.AsQueryable(false, true).Where(q => q.Stat == 0);
return ResultOutput.Ok(rtnlist);
}
public static List pidlist(List list, long pid, List dwgllist)
{
var plist = new List();
var dwgllist1 = new List();
//通过监管机构查询查询单位
if (dwgllist != null)
dwgllist1 = dwgllist.Where(q => q.mechanismId == pid).ToList();
//监管机构查询下级
foreach (var item in list.Where(q => q.ParentUnitCode == pid).ToList())
{
plist.Add(new
{
Id = item.Id,
ParentUnitCode = item.ParentUnitCode,
NameEntity = item.NameEntity,
UnitCode = item.UnitCode,
dw = item.Stat == 0 ? true : false,
children = pidlist(list, item.Id, dwgllist)
});
}
//单位查找下级
if (dwgllist1 != null)
{
foreach (var item in dwgllist1.Where(q => q.ParentUnitCode == 0 || q.ParentUnitCode == null).ToList())
{
plist.Add(new
{
Id = item.Id,
ParentUnitCode = item.ParentUnitCode,
NameEntity = item.NameEntity,
UnitCode = item.UnitCode,
dw = false,
children = pidlist(dwgllist.Where(q => q.ParentUnitCode != 0 || q.ParentUnitCode != null).ToList(), item.Id, null)
});
}
}
return plist;
}
///
/// 查询用户信息
///
///
[Login]
public async Task GetUserInfoAsync()
{
if (!(User?.Id > 0)) return ResultOutput.NotOk("未登录!");
var authUserInfoOutput = new AuthUserInfoOutput
{
//用户信息
User = await _appUserRepository.FetchAsync(w => new AuthUserProfileDto()
{
UserName = w.UserName,
NickName = w.NickName,
Avatar = w.Avatar
}, w => w.Id == User.Id)
};
//用户菜单
var ptEnums = new[] { PermissionTypeEnum.Group, PermissionTypeEnum.Menu };
authUserInfoOutput.Menus = GetPermissionInfo(0, ptEnums)
.Select(w => new AuthUserMenuDto()
{
ViewPath = w,
}).ToList();
//用户权限点
authUserInfoOutput.Permissions = GetPermissionInfo(1, PermissionTypeEnum.Dot);
return ResultOutput.Ok(authUserInfoOutput);
}
///
/// 账号密码登录
///
///
///
[HttpPost]
[AllowAnonymous]
[NoOperationLog]
public async Task LoginAsync(AuthLoginInput input)
{
var user = await _appUserRepository.FindAsync(a => a.UserName == input.UserName && a.DataStatus == DataStatusEnum.Normal && (a.ChatPersonType == ChatPersonTypeEnum.Admin || a.ChatPersonType == ChatPersonTypeEnum.Supervisor));
if (user == null) return ResultOutput.NotOk($"{input.UserName}:用户不存在!");
return await LoginCommon(user, input.Password);
}
///
/// 手机号登录
///
///
///
[HttpPost]
[AllowAnonymous]
[NoOperationLog]
public async Task LoginWithPhoneAsync(AuthLoginWithPhoneInput input)
{
var user = await _appUserRepository.FindAsync(a => (input.UnitId == null || a.UnitId.Equals(input.UnitId)) && (a.UserName == input.UserName || a.Phone == input.UserName) && a.DataStatus == DataStatusEnum.Normal && (a.ChatPersonType == ChatPersonTypeEnum.Admin || a.ChatPersonType == ChatPersonTypeEnum.Supervisor));
if (user == null) return ResultOutput.NotOk($"用户不存在,或者无权限登录!");
user.CId = input.CId;
return await LoginCommon(user, input.Password);
}
///
/// 注销/推出登录
///
///
public async Task LoginOutAsync()
{
await User.LoginOutAsync();
return ResultOutput.Ok();
}
///
/// 刷新Token
/// 以旧换新
///
///
///
[HttpGet]
[AllowAnonymous]
public async Task Refresh([BindRequired] string token)
{
var userClaims = LazyGetRequiredService().Decode(token);
if (userClaims == null || userClaims.Length == 0) return ResultOutput.NotOk();
var refreshExpires = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.refreshExpires)?.Value;
if (refreshExpires.IsNull()) return ResultOutput.NotOk();
if (refreshExpires.ToLong() <= DateTime.Now.ToTimestamp()) return ResultOutput.NotOk("登录信息已过期");
var userId = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.userId)?.Value;
if (userId.IsNull()) return ResultOutput.NotOk("登录信息已失效");
ResultOutput? output = await LazyGetRequiredService().GetLoginUserAsync(userId.ToLong());
string newToken = await GetToken(output?.Data);
return ResultOutput.Ok(new { token = newToken });
}
///
/// 获取验证数据
///
///
[HttpGet]
[AllowAnonymous]
[NoOperationLog]
[EnableCors(AdminConstant.allowAnyPolicyName)]
public async Task GetCaptcha()
{
using (MiniProfiler.Current.Step("获取滑块验证"))
{
var data = await _captchaTool.GetAsync(CacheKey.captchaKey);
return ResultOutput.Ok(data);
}
}
///
/// 检查验证数据
///
///
[HttpGet]
[AllowAnonymous]
[NoOperationLog]
[EnableCors(AdminConstant.allowAnyPolicyName)]
public async Task CheckCaptcha([FromQuery] CaptchaInput input)
{
input.CaptchaKey = CacheKey.captchaKey;
var result = await _captchaTool.CheckAsync(input);
return ResultOutput.Result(result);
}
///
/// 获取登录信息
///
///
///
///
public async Task GetUserValidateInfoAsync(long id)
{
return await _appUserRepository.FetchAsync(x => new UserValidateDto()
{
Id = x.Id,
UserName = x.UserName,
//Status = x.Status,
Name = x.Name,
//RoleIds = x.RoleIds,
ValidationVersion = InfraHelper.Hash.GetHashedString(HashTypeEnum.Md5, x.UserName + x.Password)
}, x => x.Id == id);
}
///
/// 获取权限信息公共逻辑方法
///
///
/// 返回类型(0:ViewPath、1:PermissionCode)
///
public async Task> GetPermissionItems(int type = 0, params PermissionTypeEnum[] ptEnums)
{
var data = (from pr in _appPermissionRepository.Where(w => ptEnums.Contains(w.Type))
join rp in _appRolePermissionRepository.AsQueryable(false, true) on pr.Id equals rp.PermissionId
join ur in _appUserRoleRepository.AsQueryable(false, true) on new { a = rp.RoleId, b = User.Id } equals new { a = ur.RoleId, b = ur.UserId }
join v in _appViewRepository.AsQueryable(false, true) on pr.ViewId equals v.Id
select new
{
pr,
rp,
ur,
v
}).ToList();
List result = new();
if (type == 0) result = data.Select(w => new PermissionItem()
{
Role = w.ur.RoleId.ToString(),
Url = w.v.Path
}).ToList();
else if (type == 1) data.Select(w => new PermissionItem()
{
Role = w.pr.Code,
Url = w.v.Path
}).ToList();
return result;
}
///
/// 根据身份证号获取电话号码
///
///
///
[HttpGet]
[AllowAnonymous]
public async Task GetPhoneByIDCard(string idCard)
{
var user = await _appUserRepository.FindAsync(a => a.ChatPersonType == ChatPersonTypeEnum.SupervisedPerson && a.IdCard == idCard);
return ResultOutput.Ok(user?.Phone);
}
#region Private
///
/// 获得token
///
/// 用户信息
///
[NonAction]
public async Task GetToken(AuthLoginOutput? user)
{
if (user == null) return string.Empty;
var roles = (await _userService.IsAdmin(user.Id)).Roles.Select(w => w.Id).ToList();
string limits = _appUnitRepository.AsQueryable(false, true).Where(a => a.Id == user.UnitId).Select(a => a.limits).FirstOrDefault();
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var timeLogin = Convert.ToInt64(ts.TotalMilliseconds).ToString();
var token = LazyGetRequiredService().Create(new[]
{
new Claim(ClaimAttributes.userId, user.Id.ToString()),
new Claim(ClaimAttributes.userName, user.UserName!),
new Claim(ClaimAttributes.userUnitId, user.UnitId.ToString()),
new Claim(ClaimAttributes.userDeptcodeId, user.DeptcodeId.ToString()),
new Claim(ClaimAttributes.userNickName, user?.NickName??"") ,
new Claim(ClaimAttributes.avatar,user?.Avatar??"") ,
new Claim(ClaimAttributes.roles,JsonConvert.SerializeObject(roles)),
new Claim(ClaimAttributes.orgs,JsonConvert.SerializeObject(Array.Empty())) ,
new Claim(ClaimAttributes.phone,user?.Phone??""),
new Claim(ClaimAttributes.logtime,timeLogin),
new Claim(ClaimAttributes.limits,limits),
new Claim(ClaimAttributes.IsAdmin,user.IsAdmin?"true":"false"),
new Claim(ClaimAttributes.personType,user?.ChatPersonType.ToString()!)
});
return token;
}
///
/// 获取权限信息公共逻辑方法
///
///
/// 返回类型(0:ViewPath、1:PermissionCode)
///
private List GetPermissionInfo(int type = 0, params PermissionTypeEnum[] ptEnums)
{
var data = (from pr in _appPermissionRepository.Where(w => ptEnums.Contains(w.Type))
join rp in _appRolePermissionRepository.AsQueryable(false, true) on pr.Id equals rp.PermissionId
join ur in _appUserRoleRepository.AsQueryable(false, true) on new { a = rp.RoleId, b = User.Id } equals new { a = ur.RoleId, b = ur.UserId }
join v in _appViewRepository.AsQueryable(false, true) on pr.ViewId equals v.Id
select new
{
pr,
rp,
ur,
v
}).ToList();
List result = new();
if (type == 0) result = data.Select(w => w.v.Path).ToList();
else if (type == 1) result = data.Select(w => w.pr.Code).ToList();
return result;
}
///
/// 登录公共逻辑方法(一个设备只能登录一次)
///
///
///
///
///
/// mac地址 + cookie
/// 移动就就设备id
/// PC端账号token做踢线处理 ,只能登陆一次,如果其他地方登陆了 更换token
///
private async Task LoginCommon(AppUser user, string password)
{
//var userLoginKey = string.Format(CacheKey.AdminUserLogin, user.Id,);
//var redisToken = await Cache.GetAsync(userLoginKey) ?? "";
//if (redisToken != User.GetToken()) ResultOutput.NotOk("同一个设备只能登录一次");
//if (!redisToken.IsNull()) return ResultOutput.Ok(new { redisToken });
//pc端和监管人员手机端只能登录监管人和admin,不能让被监管人登录
var isAdmin = await _userService.IsAdmin(user.Id);
if (!isAdmin.IsAdmin && !isAdmin.Roles.Any(w => w.Code is "supervisor" or "admin")) return ResultOutput.NotOk($"被监管人员{user.UserName}:无权限登录!");
var sw = new Stopwatch();
sw.Start();
if (InfraHelper.Hash.GetHashedString(HashTypeEnum.Md5, password) != user.Password) return ResultOutput.NotOk("密码输入有误!");
//更新手机唯一标识码
if (!string.IsNullOrEmpty(user.CId))
{
//var userda = _userService.GetLoginUserAsync(user.Id);
await _appUserRepository.UpdateAsync(user);
}
var authLoginOutput = Mapper.Map(user);
authLoginOutput.IsAdmin = isAdmin.IsAdmin;
var token = await GetToken(authLoginOutput);
sw.Stop();
//channel
var channelWriter = ChannelHelper.Instance.Writer;
{
//添加登录日志
var httpContext = HttpContextHelper.GetCurrentHttpContext();
var loginLogAddInput = new LoginLogAddInput
{
CreatedUserName = user.Name,
ElapsedMilliseconds = sw.ElapsedMilliseconds,
Status = true,
CreatedUserId = authLoginOutput.Id,
NickName = authLoginOutput.NickName,
TenantId = authLoginOutput.TenantId,
Device = httpContext?.Request.Headers["device"].FirstOrDefault() ?? "web",
IP = httpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString()
};
await channelWriter.WriteAsync(loginLogAddInput);
//await LazyGetRequiredService().AddAsync(loginLogAddInput);
}
//将token写入Redis
//await Cache.SetAsync(userLoginKey, token, TimeSpan.FromMinutes(_jwtConfiguration.Expires));
return ResultOutput.Ok(new { token });
}
#endregion Private
}
}