浏览代码

添加域登录配置界面以及配置项和域账号验证逻辑

徐少年 2 年之前
父节点
当前提交
10f06a41ff

+ 4 - 0
Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj

@@ -80,4 +80,8 @@
     </None>
   </ItemGroup>
 
+  <ItemGroup>
+    <Folder Include="Service\" />
+  </ItemGroup>
+
 </Project>

+ 1 - 1
Admin.NET/Admin.NET.Application/Configuration/Database.json

@@ -8,7 +8,7 @@
       {
         //"ConfigId": "1300000000001", // 默认库标识-禁止修改
         "DbType": "Sqlite", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access、OpenGauss、QuestDB、HG、ClickHouse、GBase、Odbc、Custom
-        "ConnectionString": "DataSource=./Admin.NET.db", // 库连接字符串
+        "ConnectionString": "DataSource=F:\\CodeProject\\gkxsn.com\\Admin.NET\\Admin.NET\\Admin.NET.Web.Entry\\bin\\Debug\\net6.0Admin.NET.db", // 库连接字符串
         //"SlaveConnectionConfigs": [ // 读写分离/主从
         //	{
         //		"HitRate": 10,

+ 1 - 0
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -34,6 +34,7 @@
     <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="6.0.29" />
     <PackageReference Include="NEST" Version="7.17.5" />
     <PackageReference Include="NewLife.Redis" Version="5.6.2024.402" />
+    <PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
     <PackageReference Include="OnceMi.AspNetCore.OSS" Version="1.1.9" />
     <PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
     <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.1" />

+ 5 - 0
Admin.NET/Admin.NET.Core/Const/CommonConst.cs

@@ -73,4 +73,9 @@ public class CommonConst
     /// 开启全局脱敏处理(默认不开启)
     /// </summary>
     public static bool SysSensitiveDetection = false;
+
+    /// <summary>
+    /// 开启域登录验证
+    /// </summary>
+    public const string SysDomainLogin= "sys_domain_login";
 }

+ 66 - 0
Admin.NET/Admin.NET.Core/Entity/SysLdap.cs

@@ -0,0 +1,66 @@
+// 大名科技(天津)有限公司 版权所有
+//
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
+//
+// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
+
+using static SKIT.FlurlHttpClient.Wechat.Api.Models.CgibinExpressBusinessAccountGetAllResponse.Types;
+
+namespace Admin.NET.Core;
+/// <summary>
+/// 系统域登录信息配置表
+/// </summary>
+[SugarTable(null, "系统域登录信息配置表")]
+[SysTable]
+public class SysLdap: EntityTenant
+{
+    /// <summary>
+    /// 主机
+    /// </summary>
+    [SugarColumn(ColumnDescription = "主机", Length = 128)]
+    public string Host { get; set; }
+
+    /// <summary>
+    /// 端口
+    /// </summary>
+    [SugarColumn(ColumnDescription = "端口")]
+    public Int16 Port { get; set; }
+
+    /// <summary>
+    /// 用户搜索基准
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户搜索基准",Length =128)]
+    public string BaseDn {  get; set; }
+
+    /// <summary>
+    /// 绑定DN(有管理权限制的用户)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "绑定DN", Length =32)]
+    public string BindDn { get; set; }
+
+    /// <summary>
+    /// 绑定密码(有管理权限制的用户密码)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "绑定密码", Length = 512)]
+    public string BindPass { get; set; }
+
+    /// <summary>
+    /// 用户过滤规则
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户过滤规则", Length = 128)]
+    public string AuthFilter { get; set; } = "sAMAccountName";
+
+    /// <summary>
+    /// Ldap版本
+    /// </summary>
+    [SugarColumn(ColumnDescription = "Ldap版本")]
+    public Int16 Version { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "状态")]
+    public StatusEnum Status { get; set; } = StatusEnum.Enable;
+}

+ 36 - 0
Admin.NET/Admin.NET.Core/Entity/SysUserLdap.cs

@@ -0,0 +1,36 @@
+// 大名科技(天津)有限公司 版权所有
+//
+// 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动
+//
+// 任何基于本项目二次开发而产生的一切法律纠纷和责任,均与作者无关
+
+namespace Admin.NET.Core;
+/// <summary>
+/// 系统用户域配置表
+/// </summary>
+[SugarTable(null, "系统用户域配置表")]
+[SysTable]
+[SugarIndex("index_{table}_A", nameof(Account), OrderByType.Asc)]
+[SugarIndex("index_{table}_U", nameof(UserId), OrderByType.Asc)]
+public class SysUserLdap: EntityTenant
+{
+    /// <summary>
+    /// 用户ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "用户ID")]
+    public long UserId {  get; set; }
+    /// <summary>
+    /// 域账号
+    /// AD域对应sAMAccountName
+    /// Ldap对应uid
+    /// </summary>
+    [SugarColumn(ColumnDescription = "域账号", Length = 32)]
+    public string Account { get; set; }
+    /// <summary>
+    /// 对应employeeID(用于数据导入对照)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "对应employeeID", Length = 32)]
+    public string EmployeeId { get; set; }
+}

+ 1 - 0
Admin.NET/Admin.NET.Core/SeedData/SysConfigSeedData.cs

@@ -31,6 +31,7 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
             new SysConfig{ Id=1300000000108, Name="Token过期时间", Code="sys_token_expire", Value="10080", SysFlag=YesNoEnum.Y, Remark="Token过期时间(分钟)", OrderNo=8, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000109, Name="刷新Token过期时间", Code="sys_refresh_token_expire", Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=9, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
             new SysConfig{ Id=1300000000110, Name="发送异常日志邮件", Code="sys_error_mail", Value="True", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=10, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+            new SysConfig{ Id=1300000000111, Name="开启域登录验证", Code="sys_domain_login", Value="True", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=11, GroupCode="Default", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
         };
     }
 }

+ 7 - 0
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -82,6 +82,13 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=1310000000184, Pid=1310000000181, Title="增加", Permission="sysWechatUser:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=1310000000185, Pid=1310000000181, Title="删除", Permission="sysWechatUser:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
 
+            new SysMenu{ Id=1310000000191, Pid=1310000000101, Title="AD域配置", Path="/system/sysldap", Name="sysLdap", Component="/system/ldap/index",Icon="ele-Management", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=160 },
+            new SysMenu{ Id=1310000000192, Pid=1310000000191, Title="查询", Permission="sysLdap:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=1310000000193, Pid=1310000000191, Title="详情", Permission="sysLdap:detail", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
+            new SysMenu{ Id=1310000000193, Pid=1310000000191, Title="编辑", Permission="sysLdap:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
+            new SysMenu{ Id=1310000000194, Pid=1310000000191, Title="增加", Permission="sysLdap:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
+            new SysMenu{ Id=1310000000195, Pid=1310000000191, Title="删除", Permission="sysLdap:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=140 },
+
             new SysMenu{ Id=1310000000301, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=11000 },
 
             new SysMenu{ Id=1310000000311, Pid=1310000000301, Title="租户管理", Path="/platform/tenant", Name="sysTenant", Component="/system/tenant/index", Icon="ele-School", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

+ 93 - 0
Admin.NET/Admin.NET.Core/Service/Auth/Dto/SysLdapDto.cs

@@ -0,0 +1,93 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统域登录信息配置表输出参数
+/// </summary>
+public class SysLdapDto
+    {
+        /// <summary>
+        /// 主键Id
+        /// </summary>
+        public long Id { get; set; }
+        
+        /// <summary>
+        /// 主机
+        /// </summary>
+        public string Host { get; set; }
+        
+        /// <summary>
+        /// 端口
+        /// </summary>
+        public Int16 Port { get; set; }
+        
+        /// <summary>
+        /// 用户搜索基准
+        /// </summary>
+        public string BaseDn { get; set; }
+        
+        /// <summary>
+        /// 绑定DN
+        /// </summary>
+        public string BindDn { get; set; }
+        
+        /// <summary>
+        /// 绑定密码
+        /// </summary>
+        public string BindPass { get; set; }
+        
+        /// <summary>
+        /// 用户过滤规则
+        /// </summary>
+        public string AuthFilter { get; set; }
+        
+        /// <summary>
+        /// Ldap版本
+        /// </summary>
+        public Int16 Version { get; set; }
+        
+        /// <summary>
+        /// 状态
+        /// </summary>
+        public long Status { get; set; }
+        
+        /// <summary>
+        /// 租户Id
+        /// </summary>
+        public long? TenantId { get; set; }
+        
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime? CreateTime { get; set; }
+        
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public DateTime? UpdateTime { get; set; }
+        
+        /// <summary>
+        /// 创建者Id
+        /// </summary>
+        public long? CreateUserId { get; set; }
+        
+        /// <summary>
+        /// 创建者姓名
+        /// </summary>
+        public string? CreateUserName { get; set; }
+        
+        /// <summary>
+        /// 修改者Id
+        /// </summary>
+        public long? UpdateUserId { get; set; }
+        
+        /// <summary>
+        /// 修改者姓名
+        /// </summary>
+        public string? UpdateUserName { get; set; }
+        
+        /// <summary>
+        /// 软删除
+        /// </summary>
+        public bool IsDelete { get; set; }
+        
+    }

+ 194 - 0
Admin.NET/Admin.NET.Core/Service/Auth/Dto/SysLdapInput.cs

@@ -0,0 +1,194 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统域登录信息配置表基础输入参数
+/// </summary>
+public class SysLdapBaseInput
+    {
+        /// <summary>
+        /// 主机
+        /// </summary>
+        public virtual string Host { get; set; }
+        
+        /// <summary>
+        /// 端口
+        /// </summary>
+        public virtual Int16 Port { get; set; }
+        
+        /// <summary>
+        /// 用户搜索基准
+        /// </summary>
+        public virtual string BaseDn { get; set; }
+        
+        /// <summary>
+        /// 绑定DN
+        /// </summary>
+        public virtual string BindDn { get; set; }
+        
+        /// <summary>
+        /// 绑定密码
+        /// </summary>
+        public virtual string BindPass { get; set; }
+        
+        /// <summary>
+        /// 用户过滤规则
+        /// </summary>
+        public virtual string AuthFilter { get; set; }
+        
+        /// <summary>
+        /// Ldap版本
+        /// </summary>
+        public virtual Int16 Version { get; set; }
+        
+        /// <summary>
+        /// 状态
+        /// </summary>
+        public virtual long Status { get; set; }
+        
+        /// <summary>
+        /// 租户Id
+        /// </summary>
+        public virtual long? TenantId { get; set; }
+        
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public virtual DateTime? CreateTime { get; set; }
+        
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public virtual DateTime? UpdateTime { get; set; }
+        
+        /// <summary>
+        /// 创建者Id
+        /// </summary>
+        public virtual long? CreateUserId { get; set; }
+        
+        /// <summary>
+        /// 创建者姓名
+        /// </summary>
+        public virtual string? CreateUserName { get; set; }
+        
+        /// <summary>
+        /// 修改者Id
+        /// </summary>
+        public virtual long? UpdateUserId { get; set; }
+        
+        /// <summary>
+        /// 修改者姓名
+        /// </summary>
+        public virtual string? UpdateUserName { get; set; }
+        
+        /// <summary>
+        /// 软删除
+        /// </summary>
+        public virtual bool IsDelete { get; set; }
+        
+    }
+
+    /// <summary>
+    /// 系统域登录信息配置表分页查询输入参数
+    /// </summary>
+    public class SysLdapInput : BasePageInput
+    {
+        /// <summary>
+        /// 关键字查询
+        /// </summary>
+        public string? SearchKey { get; set; }
+
+        /// <summary>
+        /// 主机
+        /// </summary>
+        public string? Host { get; set; }
+        
+    }
+
+    /// <summary>
+    /// 系统域登录信息配置表增加输入参数
+    /// </summary>
+    public class AddSysLdapInput : SysLdapBaseInput
+    {
+        /// <summary>
+        /// 主机
+        /// </summary>
+        [Required(ErrorMessage = "主机不能为空")]
+        public override string Host { get; set; }
+        
+        /// <summary>
+        /// 端口
+        /// </summary>
+        [Required(ErrorMessage = "端口不能为空")]
+        public override Int16 Port { get; set; }
+        
+        /// <summary>
+        /// 用户搜索基准
+        /// </summary>
+        [Required(ErrorMessage = "用户搜索基准不能为空")]
+        public override string BaseDn { get; set; }
+        
+        /// <summary>
+        /// 绑定DN
+        /// </summary>
+        [Required(ErrorMessage = "绑定DN不能为空")]
+        public override string BindDn { get; set; }
+        
+        /// <summary>
+        /// 绑定密码
+        /// </summary>
+        [Required(ErrorMessage = "绑定密码不能为空")]
+        public override string BindPass { get; set; }
+        
+        /// <summary>
+        /// 用户过滤规则
+        /// </summary>
+        [Required(ErrorMessage = "用户过滤规则不能为空")]
+        public override string AuthFilter { get; set; }
+        
+        /// <summary>
+        /// Ldap版本
+        /// </summary>
+        [Required(ErrorMessage = "Ldap版本不能为空")]
+        public override Int16 Version { get; set; }
+        
+        /// <summary>
+        /// 状态
+        /// </summary>
+        [Required(ErrorMessage = "状态不能为空")]
+        public override long Status { get; set; }
+        
+        /// <summary>
+        /// 软删除
+        /// </summary>
+        [Required(ErrorMessage = "软删除不能为空")]
+        public override bool IsDelete { get; set; }
+        
+    }
+
+    /// <summary>
+    /// 系统域登录信息配置表删除输入参数
+    /// </summary>
+    public class DeleteSysLdapInput : BaseIdInput
+    {
+    }
+
+    /// <summary>
+    /// 系统域登录信息配置表更新输入参数
+    /// </summary>
+    public class UpdateSysLdapInput : SysLdapBaseInput
+    {
+        /// <summary>
+        /// 主键Id
+        /// </summary>
+        [Required(ErrorMessage = "主键Id不能为空")]
+        public long Id { get; set; }
+        
+    }
+
+    /// <summary>
+    /// 系统域登录信息配置表主键查询输入参数
+    /// </summary>
+    public class QueryByIdSysLdapInput : DeleteSysLdapInput
+    {
+
+    }

+ 95 - 0
Admin.NET/Admin.NET.Core/Service/Auth/Dto/SysLdapOutput.cs

@@ -0,0 +1,95 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统域登录信息配置表输出参数
+/// </summary>
+public class SysLdapOutput
+{
+    /// <summary>
+    /// 主键Id
+    /// </summary>
+    public long Id { get; set; }
+    
+    /// <summary>
+    /// 主机
+    /// </summary>
+    public string Host { get; set; }
+    
+    /// <summary>
+    /// 端口
+    /// </summary>
+    public Int16 Port { get; set; }
+    
+    /// <summary>
+    /// 用户搜索基准
+    /// </summary>
+    public string BaseDn { get; set; }
+    
+    /// <summary>
+    /// 绑定DN
+    /// </summary>
+    public string BindDn { get; set; }
+    
+    /// <summary>
+    /// 绑定密码
+    /// </summary>
+    public string BindPass { get; set; }
+    
+    /// <summary>
+    /// 用户过滤规则
+    /// </summary>
+    public string AuthFilter { get; set; }
+    
+    /// <summary>
+    /// Ldap版本
+    /// </summary>
+    public Int16 Version { get; set; }
+    
+    /// <summary>
+    /// 状态
+    /// </summary>
+    public long Status { get; set; }
+    
+    /// <summary>
+    /// 租户Id
+    /// </summary>
+    public long? TenantId { get; set; }
+    
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public DateTime? CreateTime { get; set; }
+    
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    public DateTime? UpdateTime { get; set; }
+    
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public long? CreateUserId { get; set; }
+    
+    /// <summary>
+    /// 创建者姓名
+    /// </summary>
+    public string? CreateUserName { get; set; }
+    
+    /// <summary>
+    /// 修改者Id
+    /// </summary>
+    public long? UpdateUserId { get; set; }
+    
+    /// <summary>
+    /// 修改者姓名
+    /// </summary>
+    public string? UpdateUserName { get; set; }
+    
+    /// <summary>
+    /// 软删除
+    /// </summary>
+    public bool IsDelete { get; set; }
+    
+    }
+ 
+

+ 15 - 2
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -25,6 +25,7 @@ public class SysAuthService : IDynamicApiController, ITransient
     private readonly SysConfigService _sysConfigService;
     private readonly ICaptcha _captcha;
     private readonly SysCacheService _sysCacheService;
+    private readonly SysLdapService _sysLdapService;
 
     public SysAuthService(UserManager userManager,
         SqlSugarRepository<SysUser> sysUserRep,
@@ -33,7 +34,8 @@ public class SysAuthService : IDynamicApiController, ITransient
         SysOnlineUserService sysOnlineUserService,
         SysConfigService sysConfigService,
         ICaptcha captcha,
-        SysCacheService sysCacheService)
+        SysCacheService sysCacheService,
+        SysLdapService sysLdapService)
     {
         _userManager = userManager;
         _sysUserRep = sysUserRep;
@@ -43,6 +45,7 @@ public class SysAuthService : IDynamicApiController, ITransient
         _sysConfigService = sysConfigService;
         _captcha = captcha;
         _sysCacheService = sysCacheService;
+        _sysLdapService = sysLdapService;
     }
 
     /// <summary>
@@ -88,8 +91,18 @@ public class SysAuthService : IDynamicApiController, ITransient
         // 国密SM2解密(前端密码传输SM2加密后的)
         input.Password = CryptogramUtil.SM2Decrypt(input.Password);
 
+        // 是否开启域登录验证
+        if (await _sysConfigService.GetConfigValue<bool>(CommonConst.SysDomainLogin))
+        {
+            // 判断验证码
+            if (!await _sysLdapService.Auth(tenant.Id, user.Id, input.Password))
+            {
+                _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
+                throw Oops.Oh(ErrorCodeEnum.D1000);
+            }
+        }
         // 密码是否正确
-        if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
+        else if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
         {
             if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
             {

+ 161 - 0
Admin.NET/Admin.NET.Core/Service/Auth/SysLdapService.cs

@@ -0,0 +1,161 @@
+using Novell.Directory.Ldap;
+
+namespace Admin.NET.Core;
+/// <summary>
+/// 系统域登录信息配置表服务
+/// </summary>
+[ApiDescriptionSettings(Order = 100)]
+public class SysLdapService : IDynamicApiController, ITransient
+{
+    private readonly SqlSugarRepository<SysLdap> _rep;
+    private readonly SqlSugarRepository<SysUserLdap> _repUserLdap;
+    public SysLdapService(SqlSugarRepository<SysLdap> rep, SqlSugarRepository<SysUserLdap> repUserLdap)
+    {
+        _rep = rep;
+        _repUserLdap = repUserLdap;
+    }
+
+    /// <summary>
+    /// 分页查询系统域登录信息配置表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Page")]
+    public async Task<SqlSugarPagedList<SysLdapOutput>> Page(SysLdapInput input)
+    {
+        var query = _rep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), u =>
+                u.Host.Contains(input.SearchKey.Trim())
+            )
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Host), u => u.Host.Contains(input.Host.Trim()))
+            .Select<SysLdapOutput>();
+        return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 增加系统域登录信息配置表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Add")]
+    public async Task<long> Add(AddSysLdapInput input)
+    {
+        var entity = input.Adapt<SysLdap>();
+        entity.BindPass = CryptogramUtil.Encrypt(input.BindPass);
+        await _rep.InsertAsync(entity);
+        return entity.Id;
+    }
+
+    /// <summary>
+    /// 删除系统域登录信息配置表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Delete")]
+    public async Task Delete(DeleteSysLdapInput input)
+    {
+        var entity = await _rep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        await _rep.FakeDeleteAsync(entity);   //假删除
+        //await _rep.DeleteAsync(entity);   //真删除
+    }
+
+    /// <summary>
+    /// 更新系统域登录信息配置表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    [ApiDescriptionSettings(Name = "Update")]
+    public async Task Update(UpdateSysLdapInput input)
+    {
+        var entity = input.Adapt<SysLdap>();
+        if (!string.IsNullOrEmpty(input.BindPass) && input.BindPass.Length < 32)
+        {
+            entity.BindPass = CryptogramUtil.Encrypt(input.BindPass);//未加密的字符串执行加密
+        }
+        await _rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 获取系统域登录信息配置表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "Detail")]
+    public async Task<SysLdap> Detail([FromQuery] QueryByIdSysLdapInput input)
+    {
+        return await _rep.GetFirstAsync(u => u.Id == input.Id);
+    }
+
+    /// <summary>
+    /// 获取系统域登录信息配置表列表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet]
+    [ApiDescriptionSettings(Name = "List")]
+    public async Task<List<SysLdapOutput>> List([FromQuery] SysLdapInput input)
+    {
+        return await _rep.AsQueryable().Select<SysLdapOutput>().ToListAsync();
+    }
+
+    /// <summary>
+    /// 账号验证
+    /// </summary>
+    /// <param name="userId">用户Id</param>
+    /// <param name="password">密码</param>
+    /// <param name="tenantId">租户</param>
+    /// <returns></returns>
+    [NonAction]
+    public async Task<bool> Auth(long tenantId, long userId, string password)
+    {
+        var user = await _repUserLdap.GetFirstAsync(u => u.UserId == userId && u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
+        var ldap = await _rep.GetFirstAsync(u => u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
+        LdapConnection conn = new LdapConnection();
+        try
+        {
+            conn.Connect(ldap.Host, ldap.Port);
+            conn.Bind(ldap.Version, ldap.BindDn, ldap.BindPass);
+            var userEntitys = conn.Search(ldap.BaseDn, LdapConnection.ScopeSub, $"{ldap.AuthFilter}={user.Account}", null, false);
+            string dn = string.Empty;
+            while (userEntitys.HasMore())
+            {
+                var entity = userEntitys.Next();
+                var sAMAccountName = entity.GetAttribute(ldap.AuthFilter)?.StringValue;
+
+                if (!string.IsNullOrEmpty(sAMAccountName))
+                {
+                    dn = entity.Dn;
+                    break;
+                }
+            }
+            if (string.IsNullOrEmpty(dn)) throw Oops.Oh(ErrorCodeEnum.D1002);
+            LdapAttribute attr = new LdapAttribute("userPassword", password);
+            conn.Bind(dn, password);
+        }
+        catch (LdapException e)
+        {
+            switch (e.ResultCode)
+            {
+                case LdapException.NoSuchObject:
+                case LdapException.NoSuchAttribute:
+                    throw Oops.Oh(ErrorCodeEnum.D0009);
+                case LdapException.InvalidCredentials:
+                    return false;
+                default:
+                    throw Oops.Oh(e.Message);
+            }
+        }
+        finally
+        {
+            conn.Disconnect();
+        }
+        return true;
+    }
+
+}
+

+ 50 - 0
Web/src/api/system/sysLdap.ts

@@ -0,0 +1,50 @@
+import request from '/@/utils/request';
+enum Api {
+  AddSysLdap = '/api/sysLdap/add',
+  DeleteSysLdap = '/api/sysLdap/delete',
+  UpdateSysLdap = '/api/sysLdap/update',
+  PageSysLdap = '/api/sysLdap/page',
+  DetailSysLdap = '/api/sysLdap/detail',
+}
+
+// 增加系统域登录信息配置表
+export const addSysLdap = (params?: any) =>
+	request({
+		url: Api.AddSysLdap,
+		method: 'post',
+		data: params,
+	});
+
+// 删除系统域登录信息配置表
+export const deleteSysLdap = (params?: any) => 
+	request({
+			url: Api.DeleteSysLdap,
+			method: 'post',
+			data: params,
+		});
+
+// 编辑系统域登录信息配置表
+export const updateSysLdap = (params?: any) => 
+	request({
+			url: Api.UpdateSysLdap,
+			method: 'post',
+			data: params,
+		});
+
+// 分页查询系统域登录信息配置表
+export const pageSysLdap = (params?: any) => 
+	request({
+			url: Api.PageSysLdap,
+			method: 'post',
+			data: params,
+		});
+
+// 详情系统域登录信息配置表
+export const detailSysLdap = (id: any) => 
+	request({
+			url: Api.DetailSysLdap,
+			method: 'get',
+			data: { id },
+		});
+
+

+ 177 - 0
Web/src/views/system/ldap/component/editDialog.vue

@@ -0,0 +1,177 @@
+<template>
+	<div class="sysLdap-container">
+		<el-dialog v-model="isShowDialog" :width="800" draggable="">
+			<template #header>
+				<div style="color: #fff">
+					<!--<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>-->
+					<span>{{ props.title }}</span>
+				</div>
+			</template>
+			<el-form :model="ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules">
+				<el-row :gutter="35">
+					<el-form-item v-show="false">
+						<el-input v-model="ruleForm.id" />
+					</el-form-item>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="主机" prop="host">
+							<el-input v-model="ruleForm.host" placeholder="请输入主机" maxlength="128" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="端口" prop="port">
+							<el-input v-model="ruleForm.port" placeholder="请输入端口" maxlength="0" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户搜索基准" prop="baseDn">
+							<el-input v-model="ruleForm.baseDn" placeholder="请输入用户搜索基准" maxlength="128" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="用户过滤规则" prop="authFilter">
+							<el-input v-model="ruleForm.authFilter" placeholder="请输入用户过滤规则" maxlength="128" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="绑定DN" prop="bindDn">
+							<el-input v-model="ruleForm.bindDn" placeholder="请输入有域管理权限的账户" maxlength="32" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="绑定密码" prop="bindPass">
+							<el-input v-model="ruleForm.bindPass" placeholder="请输入有域管理权限的密码" maxlength="512" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="Ldap版本" prop="version">
+							<el-input v-model="ruleForm.version" placeholder="请输入Ldap版本" maxlength="0" show-word-limit clearable />
+							
+						</el-form-item>
+						
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="状态" prop="status">
+							<el-switch v-model="ruleForm.status" active-text="是" inactive-text="否" />
+							
+						</el-form-item>
+						
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel">取 消</el-button>
+					<el-button type="primary" @click="submit">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+<style scoped>
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>
+<script lang="ts" setup>
+	import { ref,onMounted } from "vue";
+	import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils';
+	import { ElMessage } from "element-plus";
+	import type { FormRules } from "element-plus";
+	import { addSysLdap, updateSysLdap, detailSysLdap } from "../../../../api/system/sysLdap";
+
+	//父级传递来的参数
+	var props = defineProps({
+		title: {
+		type: String,
+		default: "",
+	},
+	});
+	//父级传递来的函数,用于回调
+	const emit = defineEmits(["reloadTable"]);
+	const ruleFormRef = ref();
+	const isShowDialog = ref(false);
+	const ruleForm = ref<any>({});
+	//自行添加其他规则
+	const rules = ref<FormRules>({
+		host: [{required: true, message: '请输入主机!', trigger: 'blur',},],
+		port: [{required: true, message: '请输入端口!', trigger: 'blur',},],
+		baseDn: [{required: true, message: '请输入用户搜索基准!', trigger: 'blur',},],
+		bindDn: [{required: true, message: '请输入绑定DN!', trigger: 'blur',},],
+		bindPass: [{required: true, message: '请输入绑定密码!', trigger: 'blur',},],
+		authFilter: [{required: true, message: '请输入用户过滤规则!', trigger: 'blur',},],
+		version: [{required: true, message: '请输入Ldap版本!', trigger: 'blur',},],
+	});
+
+	// 打开弹窗
+	const openDialog = async (row: any) => {
+		// ruleForm.value = JSON.parse(JSON.stringify(row));
+		// 改用detail获取最新数据来编辑
+		let rowData = JSON.parse(JSON.stringify(row));
+		if (rowData.id)
+			ruleForm.value = (await detailSysLdap(rowData.id)).data.result;
+		else
+			ruleForm.value = rowData;
+		isShowDialog.value = true;
+	};
+
+	// 关闭弹窗
+	const closeDialog = () => {
+		emit("reloadTable");
+		isShowDialog.value = false;
+	};
+
+	// 取消
+	const cancel = () => {
+		isShowDialog.value = false;
+	};
+
+	// 提交
+	const submit = async () => {
+		ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => {
+			if (isValid) {
+				let values = ruleForm.value;
+				if (ruleForm.value.id == undefined || ruleForm.value.id == null || ruleForm.value.id == "" || ruleForm.value.id == 0) {
+					await addSysLdap(values);
+				} else {
+					await updateSysLdap(values);
+				}
+				closeDialog();
+			} else {
+				ElMessage({
+					message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`,
+					type: "error",
+				});
+			}
+		});
+	};
+
+
+
+
+
+
+
+	// 页面加载时
+	onMounted(async () => {
+	});
+
+	//将属性或者函数暴露给父组件
+	defineExpose({ openDialog });
+</script>
+
+
+
+

+ 193 - 0
Web/src/views/system/ldap/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="sysLdap-container">
+    <el-card shadow="hover" :body-style="{ paddingBottom: '0' }"> 
+      <el-form :model="queryParams" ref="queryForm" labelWidth="90">
+        <el-row>
+          <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
+            <el-form-item label="关键字">
+              <el-input v-model="queryParams.searchKey" clearable="" placeholder="请输入模糊查询关键字"/>
+              
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI">
+            <el-form-item label="主机">
+              <el-input v-model="queryParams.host" clearable="" placeholder="请输入主机"/>
+              
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" class="mb10">
+            <el-form-item>
+              <el-button-group style="display: flex; align-items: center;">
+                <el-button type="primary"  icon="ele-Search" @click="handleQuery" v-auth="'sysLdap:page'"> 查询 </el-button>
+                      <el-button icon="ele-Refresh" @click="() => queryParams = {}"> 重置 </el-button>
+                        <el-button icon="ele-ZoomIn" @click="changeAdvanceQueryUI" v-if="!showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button>
+                        <el-button icon="ele-ZoomOut" @click="changeAdvanceQueryUI" v-if="showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button>
+                <el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @click="openAddSysLdap" v-auth="'sysLdap:add'"> 新增 </el-button>
+                
+              </el-button-group>
+            </el-form-item>
+            
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+    <el-card class="full-table" shadow="hover" style="margin-top: 5px">
+      <el-table
+				:data="tableData"
+				style="width: 100%"
+				v-loading="loading"
+				tooltip-effect="light"
+				row-key="id"
+                @sort-change="sortChange"
+				border="">
+        <el-table-column type="index" label="序号" width="55" align="center"/>
+        <el-table-column prop="host" label="主机" width="140"  show-overflow-tooltip="" />
+        <el-table-column prop="port" label="端口" width="140"  show-overflow-tooltip="" />
+        <el-table-column prop="baseDn" label="用户搜索基准" width="90"  show-overflow-tooltip="" />
+        <el-table-column prop="bindDn" label="绑定DN" width="140"  show-overflow-tooltip="" />
+        <el-table-column prop="bindPass" label="绑定密码" width="140"  show-overflow-tooltip="" />
+        <el-table-column prop="authFilter" label="用户过滤规则" width="90"  show-overflow-tooltip="" />
+        <el-table-column prop="version" label="Ldap版本" width="90"  show-overflow-tooltip="" />
+        <el-table-column prop="status" label="状态" width="120"  show-overflow-tooltip="">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status"> 是 </el-tag>
+            <el-tag type="danger" v-else> 否 </el-tag>
+            
+          </template>
+          
+        </el-table-column>
+        <el-table-column label="操作" width="140" align="center" fixed="right" show-overflow-tooltip="" v-if="auth('sysLdap:update') || auth('sysLdap:delete')">
+          <template #default="scope">
+            <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditSysLdap(scope.row)" v-auth="'sysLdap:update'"> 编辑 </el-button>
+            <el-button icon="ele-Delete" size="small" text="" type="primary" @click="delSysLdap(scope.row)" v-auth="'sysLdap:delete'"> 删除 </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+				v-model:currentPage="tableParams.page"
+				v-model:page-size="tableParams.pageSize"
+				:total="tableParams.total"
+				:page-sizes="[10, 20, 50, 100, 200, 500]"
+				small=""
+				background=""
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+	/>
+      <printDialog
+        ref="printDialogRef"
+        :title="printSysLdapTitle"
+        @reloadTable="handleQuery" />
+      <editDialog
+        ref="editDialogRef"
+        :title="editSysLdapTitle"
+        @reloadTable="handleQuery"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup="" name="sysLdap">
+  import { ref } from "vue";
+  import { ElMessageBox, ElMessage } from "element-plus";
+  import { auth } from '/@/utils/authFunction';
+  import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils';
+  import { formatDate } from '/@/utils/formatTime';
+
+
+  import printDialog from '/@/views/system/print/component/hiprint/preview.vue'
+  import editDialog from '/@/views/system/ldap/component/editDialog.vue'
+  import { pageSysLdap, deleteSysLdap } from '../../../api/system/sysLdap';
+
+
+  const showAdvanceQueryUI = ref(false);
+  const printDialogRef = ref();
+  const editDialogRef = ref();
+  const loading = ref(false);
+  const tableData = ref<any>([]);
+  const queryParams = ref<any>({});
+  const tableParams = ref({
+    page: 1,
+    pageSize: 10,
+    total: 0,
+  });
+
+  const printSysLdapTitle = ref("");
+  const editSysLdapTitle = ref("");
+
+  // 改变高级查询的控件显示状态
+  const changeAdvanceQueryUI = () => {
+    showAdvanceQueryUI.value = !showAdvanceQueryUI.value;
+  }
+  
+
+  // 查询操作
+  const handleQuery = async () => {
+    loading.value = true;
+    var res = await pageSysLdap(Object.assign(queryParams.value, tableParams.value));
+    tableData.value = res.data.result?.items ?? [];
+    tableParams.value.total = res.data.result?.total;
+    loading.value = false;
+  };
+
+  // 列排序
+  const sortChange = async (column: any) => {
+	queryParams.value.field = column.prop;
+	queryParams.value.order = column.order;
+	await handleQuery();
+  };
+
+  // 打开新增页面
+  const openAddSysLdap = () => {
+    editSysLdapTitle.value = '添加系统域登录信息配置表';
+    editDialogRef.value.openDialog({});
+  };
+
+  // 打开打印页面
+  const openPrintSysLdap = async (row: any) => {
+    printSysLdapTitle.value = '打印系统域登录信息配置表';
+  }
+  
+  // 打开编辑页面
+  const openEditSysLdap = (row: any) => {
+    editSysLdapTitle.value = '编辑系统域登录信息配置表';
+    editDialogRef.value.openDialog(row);
+  };
+
+  // 删除
+  const delSysLdap = (row: any) => {
+    ElMessageBox.confirm(`确定要删除吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+  .then(async () => {
+    await deleteSysLdap(row);
+    handleQuery();
+    ElMessage.success("删除成功");
+  })
+  .catch(() => {});
+  };
+
+  // 改变页面容量
+  const handleSizeChange = (val: number) => {
+    tableParams.value.pageSize = val;
+    handleQuery();
+  };
+
+  // 改变页码序号
+  const handleCurrentChange = (val: number) => {
+    tableParams.value.page = val;
+    handleQuery();
+  };
+
+  handleQuery();
+</script>
+<style scoped>
+:deep(.el-ipnut),
+:deep(.el-select),
+:deep(.el-input-number) {
+	width: 100%;
+}
+</style>
+