EasyAdminBlazor
EasyAdminBlazor 是一个基于 .NET 10 + Blazor 技术栈的企业级后台管理框架,由 BootstrapBlazor 组件库驱动 UI,FreeSql 提供数据访问能力。功能全面、开箱即用、扩展灵活,是个人开发者接私活、外包项目的利器。
🌐 官方网站:https://easyadmim.wang-zhan.com.cn
✨ 核心特性
🧩 模块化架构
核心功能与扩展功能解耦,通过 NuGet 扩展按需安装,避免功能臃肿:
| 模块 | 说明 |
|---|---|
| 用户管理 | 用户增删改查、状态管理、多端登录检测 |
| 角色管理 | 基于角色的权限分配、管理员标识 |
| 菜单管理 | 无限级菜单树、按钮级权限、可视化配置 |
| 组织架构 | 树形部门管理、数据权限隔离 |
| 配置管理 | 系统参数动态配置,支持数据库存储 |
| 字典管理 | 通用字典表,支持下拉/多选/级联 |
| 日志管理 | 操作日志、登录日志、错误日志,自动记录 |
| 文件管理 | 本地文件上传/下载、图片压缩/WebP 转换、分组管理 |
🌐 多租户(SaaS)
- 独立数据库:租户使用独立数据库
- 按域名解析:根据请求的 Host 自动识别当前租户,零配置切换
- 菜单权限隔离:每个租户可独立分配功能菜单
- 文件存储隔离:上传文件按租户 code 分目录存储
🌍 多语言
内置本地化支持,可动态切换界面语言,满足国际化需求。
💬 实时聊天
基于 SignalR 实现站内即时通讯,支持消息实时推送、未读提醒。
📋 计划任务
集成 FreeScheduler,支持 Cron 表达式和特性标注 [Scheduler],可视化任务管理。
🔌 可插拔扩展
通过扩展项目按需添加能力:
| 扩展 | 功能 |
|---|---|
EasyAdminBlazor.Mail |
SMTP 邮件发送 |
EasyAdminBlazor.Captcha |
表单验证码 |
EasyAdminBlazor.Redis |
Redis 缓存/消息持久化 |
EasyAdminBlazor.FusionCache |
混合缓存加速 |
EasyAdminBlazor.Chat |
实时聊天插件 |
EasyAdminBlazor.MultiTenant |
多租户支持 |
EasyAdminBlazor.Scheduler |
后台任务调度 |
EasyAdminBlazor.HtmlEditor |
TinyMCE 富文本编辑器 |
EasyAdminBlazor.WeChat |
微信小程序、微信支付集成 |
🛡️ 安全特性
- 密码 PBKDF2 加密存储
- Cookie 认证 + AES 加密
- CSRF 防护(Antiforgery Token)
- 管理后台路由加密(AdminRouteSecret)
- 操作日志审计
- 登录失败次数限制 + 验证码
🏗️ 技术栈
| 层级 | 技术 |
|---|---|
| 框架 | .NET 10 |
| UI | Blazor Server / BootstrapBlazor |
| ORM | FreeSql(支持 MySQL、Sqlite、PostgreSQL、SQL Server 等) |
| 实时通信 | SignalR |
| 认证 | Cookie Authentication + ASP.NET Core Identity |
| 缓存 | IMemoryCache / Redis / FusionCache |
| 任务调度 | FreeScheduler |
| 图片处理 | SixLabors.ImageSharp |
| ID 生成 | Yitter 雪花算法 |
🚀 快速开始
环境要求
- .NET 10 SDK
- MySQL 8.0+(或其他 FreeSql 支持的数据库)
- (可选)Redis
第一步:创建项目
dotnet new web -n YourProjectName
cd YourProjectName
第二步:安装 NuGet 包
# 核心包(必须)
dotnet add package EasyAdminBlazor
# 数据库支持(根据你的数据库选择其中一个)
dotnet add package FreeSql.Provider.MySqlConnector # MySQL
dotnet add package FreeSql.Provider.SqlServer # SQL Server
dotnet add package FreeSql.Provider.Sqlite # SQLite
dotnet add package FreeSql.Provider.PostgreSQL # PostgreSQL
第三步:配置 Program.cs
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
builder.AddEasyAdminBlazor(new EasyAdminBlazorOptions
{
EnableLoginCaptcha = true,
EnableMessage =true,
ShowHelp =true,
AesKey= "XME2k7nA9vJy9osiU389469Y",
AdminRouteSecret= "i4ByMAX4",
Assemblies = [typeof(Program).Assembly],
FreeSqlBuilder = a => a
.UseConnectionString(DataType.MySql, configuration["ConnectionStrings:default"])
.UseMonitorCommand(cmd => System.Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] {cmd.CommandText}\r\n"))//监听SQL语句
.UseAutoSyncStructure(true)
})
.AddEasyAdminBlazorMail() // 添加smtp邮件扩展
.AddEasyAdminBlazorTinyMCEEditor() // 编辑器扩展
.AddEasyAdminBlazorRedis(configuration["Redis:ConnectionString"]!)
.AddEasyAdminBlazorChat() // 聊天插件(依赖redis,没配置则不启用)
.AddEasyAdminBlazorWeChat() // 微信插件
.AddEasyAdminBlazorScheduler() // 计划任务扩展
.AddEasyAdminBlazorFusionCache()
.AddEasyAdminBlazorMultiTenant()
.AddEasyAdminBlazorCaptcha(options =>
{
options.Chars = "0123456789";
}); // 验证码
// Add services to the container.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// 注册 Session 服务
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.Services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddBootstrapBlazorTableExportService();
builder.Services.Configure<HubOptions>(option => {
option.MaximumReceiveMessageSize = null;
});
var app = builder.Build();
// 如果EnableLocalization=true,请取消注释以下
//var option = app.Services.GetService<IOptions<RequestLocalizationOptions>>();
//if (option != null)
//{
// app.UseRequestLocalization(option.Value);
//}
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All });
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddAdditionalAssemblies(typeof(EasyAdminBlazor._Imports).Assembly)
.AddInteractiveServerRenderMode();
// 使用 Session
app.UseSession();
app.UseChat();
app.UseEasyAdminBlazor();
app.Run();
第四步:appsettings.json 完整配置示例
{
"ConnectionStrings": {
"Default": "Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456; Initial Catalog=freesql_simple;Charset=utf8mb4; SslMode=none;Min pool size=1"
},
"Redis": {
"ConnectionString": "127.0.0.1:6379,poolsize=10"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"FileSettings": {
"DirectoryName": "uploads",
"IncludeExtension": [ ".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".webp" ],
"ExcludeExtension": [ ".exe", ".dll", ".jar", ".php", ".aspx", ".bat", ".cmd", ".vbs", ".js", ".html", ".htm" ],
"DateTimeDirectory": "yyyy/MM/dd",
"KeepOriginalFile": false, //转换为webp后是否保留原文件
"MaxImageWidth": 800,
"WebpQuality": 80
},
"SmtpSettings": {
"Server": "smtp.example.com",
"Port": 587,
"Username": "your_email@example.com",
"Password": "your_email_password",
"FromEmail": "your_email@example.com",
"EnableSsl": true
},
"Wechat": {
"App": {
"AppKey": "your_app_key",
"AppSecret": "your_app_secret"
},
"PayV3": {
"AppId": "",
"MchId": "",
"ApiV3Key": "",
"CertificatePath": "certs/apiclient_cert.pem",
"KeyPath": "certs/apiclient_key.pem",
"PubPath": "certs/pub_key.pem",
"NotifyUrl": "",
"BaseUrl": "https://api.mch.weixin.qq.com"
}
},
"TinyMCE": {
"ScriptSrc": "https://cdn.wang-zhan.cn/tinymce_8.3.2/tinymce.min.js"
},
"BootstrapBlazorOptions": {
"ToastDelay": 4000,
"MessageDelay": 4000,
"SwalDelay": 4000,
"EnableErrorLogger": true,
"FallbackCulture": "en",
"SupportedCultures": [
"zh-CN",
"en-US"
],
"TableSettings": {
"CheckboxColumnWidth": 36
},
"WebClientOptions": {
"EnableIpLocator": true
},
"IgnoreLocalizerMissing": true,
"StepSettings": {
"Short": "1",
"Int": "1",
"Long": "1",
"Float": "0.1",
"Double": "0.01",
"Decimal": "0.01"
},
"DefaultCultureInfo": "zh-CN"
}
}
第五步:在根目录的Components文件夹下添加 App.razor 页面
<!DOCTYPE html>
<html lang="en" data-bs-theme='light'>
<head>
<meta charset="utf-8" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="admin template,bootstrap,blazor,wasm,webassembly,UI,netcore,web,assembly">
<meta name="description" content="基于Bootstrap和Freesql的后台系统,用于研发企业级中后台产品。">
<meta name="author" content="gudufy (84383822@qq.com) www.wang-zhan.com.cn">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="favicon.png">
<base href="/" />
<link rel="stylesheet" href="@Assets["_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css"]" />
<link rel="stylesheet" href="@Assets["_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css"]" />
<link rel="stylesheet" href="@Assets["_content/EasyAdminBlazor/ant.css"]">
<link rel="stylesheet" href="@Assets["_content/EasyAdminBlazor/adminblazor.css"]" />
<link rel="stylesheet" href="@Assets["EasyAdminBlazor.styles.css"]" />
<link rel="stylesheet" href="@Assets["EasyAdminBlazor.Test.styles.css"]" />
<title>后台管理系统 - EasyAdminBlazor</title>
<ImportMap></ImportMap>
<HeadOutlet @rendermode="new InteractiveServerRenderMode(false)" />
</head>
<body>
<Routes @rendermode="new InteractiveServerRenderMode(false)" />
<ReconnectorOutlet ReconnectInterval="5000" @rendermode="new InteractiveServerRenderMode(false)" />
<script Src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
<script src="_framework/blazor.web.js"></script>
<script src="@Assets["_content/EasyAdminBlazor/adminblazor.js"]"></script>
<script Src="@Assets["_content/EasyAdminBlazor.HtmlEditor/TinyMCEEditor.razor.js"]"></script>
</body>
</html>
第六步:在根目录的Components文件夹下添加 Routes.razor 页面
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(MainLayout).Assembly }">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
</Router>
第六步:运行项目
dotnet run
默认管理员账号:admin,密码:123yyq。租户默认密码根据租户编码自动生成(如 vip123)。