//PermissionAttribute.cs
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Admin.NET.Application;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class PermissionAttribute : Attribute
{
/// <summary>
/// 父Id
/// </summary>
public long Pid { get; set; }
/// <summary>
/// 菜单类型(1目录 2菜单 3按钮)
/// </summary>
public MenuTypeEnum Type { get; set; } = MenuTypeEnum.Menu;
/// <summary>
/// 路由名称
/// </summary>
[MaxLength(64)]
public string? Name { get; set; }
/// <summary>
/// 路由地址
/// </summary>
[MaxLength(128)]
public string? Path { get; set; }
/// <summary>
/// 组件路径
/// </summary>
[MaxLength(128)]
public string? Component { get; set; }
/// <summary>
/// 权限标识
/// </summary>
[MaxLength(128)]
public string? Permission { get; set; }
/// <summary>
/// 菜单名称
/// </summary>
[Required, MaxLength(64)]
public virtual string Title { get; set; }
/// <summary>
/// 图标
/// </summary>
[MaxLength(128)]
public string? Icon { get; set; } = "ele-Menu";
/// <summary>
/// 是否隐藏
/// </summary>
public bool IsHide { get; set; }
/// <summary>
/// 是否缓存
/// </summary>
public bool IsKeepAlive { get; set; } = true;
/// <summary>
/// 是否固定
/// </summary>
public bool IsAffix { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNo { get; set; } = 100;
/// <summary>
/// 状态
/// </summary>
public StatusEnum Status { get; set; } = StatusEnum.Enable;
/// <summary>
/// 备注
/// </summary>
[MaxLength(256)]
public string? Remark { get; set; }
}
//IPermissionInitializer.cs
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Application;
public interface IPermissionInitializer
{
void Initialize();
}
//PermissionInitializer.cs
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
/* 按钮权限自动生成器方法介绍:
在服务类上加[Permission( Title = "菜单名称")]
在方法上加上[Permission()]
在Startup.cs文件中加
services.AddScoped<IPermissionInitializer,PermissionInitializer>();
using (var scope = app.ApplicationServices.CreateScope())
{
var initializer = scope.ServiceProvider.GetRequiredService<IPermissionInitializer>();
initializer.Initialize();
}
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Admin.NET.Application;
public class PermissionInitializer : IPermissionInitializer
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<PermissionInitializer> _logger;
public PermissionInitializer(
IServiceProvider serviceProvider,
ILogger<PermissionInitializer> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public void Initialize()
{
try
{
using (var scope = _serviceProvider.CreateScope())
{
// 获取SqlSugar仓储
var menuRepository = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysMenu>>();
_logger.LogInformation("开始扫描控制器并初始化菜单按钮权限...");
// 获取所有继承自IDynamicApiController的类型
var controllerTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.IsClass && !t.IsAbstract && typeof(IDynamicApiController).IsAssignableFrom(t))
.ToList();
var menus = new List<SysMenu>();
foreach (var controllerType in controllerTypes)
{
// 处理控制器级别的权限
var controllerPermissions = controllerType.GetCustomAttributes<PermissionAttribute>().ToList();
if (!controllerPermissions.Any())
{
continue;
}
var menuTitle = controllerPermissions[0];
var menu = menuRepository.GetFirst(m => m.Title == menuTitle.Title && m.Type == MenuTypeEnum.Menu);
if (menu == null)
{
continue;
}
// 处理方法级别的权限
var methods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.DeclaringType == controllerType);
foreach (var method in methods)
{
var methodPermissions = method.GetCustomAttributes<PermissionAttribute>();
foreach (var permAttr in methodPermissions)
{
if (string.IsNullOrWhiteSpace(permAttr.Title))
{
var displayNameAttributes = method.GetCustomAttributes<DisplayNameAttribute>().ToList();
var displayNameAttribute = displayNameAttributes.FirstOrDefault();
permAttr.Title = displayNameAttribute?.DisplayName ?? method.Name;
}
if (string.IsNullOrWhiteSpace(permAttr.Permission))
{
permAttr.Permission = controllerType.Name.Replace("Service", "").ToFirstLetterLowerCase() + "/" + method.Name.ToFirstLetterLowerCase();
}
var methodMenu = menuRepository.GetFirst(m => m.Title == permAttr.Title && m.Type == MenuTypeEnum.Btn && m.Pid == menu.Id);
if (methodMenu != null)
{
continue;
}
// 对于方法级别的权限,通常是按钮类型
methodMenu = new SysMenu
{
Pid = menu.Id,
Type = MenuTypeEnum.Btn, // 方法级别的权限通常是按钮
Name = permAttr.Name,
Path = permAttr.Path,
Component = permAttr.Component,
Permission = permAttr.Permission,
Title = permAttr.Title,
Icon = permAttr.Icon,
IsHide = permAttr.IsHide,
IsKeepAlive = permAttr.IsKeepAlive,
IsAffix = permAttr.IsAffix,
OrderNo = permAttr.OrderNo,
Status = permAttr.Status,
Remark = permAttr.Remark
};
menus.Add(methodMenu);
}
}
}
// 保存菜单和按钮权限
if (menus.Any())
{
// 获取现有菜单
var existingMenus = menuRepository.GetList();
// 过滤掉已存在的菜单和按钮
var newMenus = menus
.Where(m => !existingMenus.Any(em =>
em.Permission == m.Permission &&
em.Type == m.Type))
.ToList();
if (newMenus.Any())
{
menuRepository.InsertRange(newMenus);
_logger.LogInformation($"成功添加{newMenus.Count}个新的菜单和按钮权限");
}
}
_logger.LogInformation("菜单按钮权限初始化完成");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "初始化菜单按钮权限时发生错误");
}
}
}