配置文件(appsettings.json)
配置文件(appsettings.json)
内网宿主 ZR.Admin.WebApi 与公网宿主 ZR.Front.WebApi 各有一份 appsettings.json,大部分节点同名同义,差异见《公网宿主》。本页承接 appsettings.json 中的注释,配置含义看这里即可,JSON 里只保留一行提示。
一、加载顺序
后加载的配置源覆盖前面的:
appsettings.json(随代码入库,不放密钥)appsettings.{Environment}.json(已被.gitignore忽略,敏感值与部署差异化配置放这里)- UserSecrets(仅
ZR.Admin.WebApi的 Development 环境加载) - 环境变量(层级用双下划线,如
JwtSettings__SecretKey) - 命令行参数
除主文件外还有几个独立配置文件:
| 文件 | 节点 | 说明 |
|---|---|---|
codeGen.json | CodeGen | 代码生成;可被 CodeGen__xxx 环境变量覆盖 |
ai.json / ai.{Env}.json | AiOptions | AI(赞助模块),见《AI 使用》 |
iprate.json | ClientRateLimiting | IP/用户限流规则,需先 AddJsonFile 才生效 |
二、敏感值注入
以下键禁止明文提交,请用未入库的 appsettings.{Environment}.json 或环境变量注入:
| 键 | 要求 |
|---|---|
JwtSettings:SecretKey | HS256 要求 ≥ 32 个字符(按 ASCII 取字节);两个宿主必须不同,否则可互相伪造令牌 |
MemberJwt:SecretKey | ≥ 32 字节,仅公网宿主;未配置时拒绝签发与识别会员令牌(fail-closed,不会退化派生) |
DataProtection:CertificatePassword | PFX 口令 |
WechatPay:MerchantCertificatePrivateKey / MerchantV3Key / WechatPayPublicKey | 商户私钥、APIv3 密钥、平台公钥(PEM) |
S3:AccessKey / S3:SecretKey、ALIYUN_OSS:KEY / SECRET | 对象存储凭据 |
SmsOptions:AccessKeyId / AccessKeySecret | 短信凭据 |
MailOptions[].Password | 发件邮箱密码/授权码 |
dbConfigs[].Conn | 数据库连接串;也不要把密码写进入库的默认串 |
环境变量覆盖整串
环境变量按配置键整体覆盖,例如 dbConfigs__0__Conn 覆盖第一个数据源的完整连接串;本机开发也可用集成认证 Integrated Security=True 免密连接。
三、配置节速查
内网宿主 ZR.Admin.WebApi
| 节点 | 默认 | 说明 |
|---|---|---|
urls | http://localhost:8888 | 监听地址;改端口后前端 devServer 代理需同步 |
corsUrls | 本地两个前端地址 | 允许跨域的前端地址;http://localhost:8887 与带结尾 / 视为不同 |
dbConfigs | — | 数据源数组,见下文 |
CodeGenDbConfig | — | 代码生成库;{dbName} 是占位符,不要填具体库名 |
JwtSettings | — | 后台令牌签发/续期,见《认证鉴权流程》 |
TwoFactor | 全可缺省 | 双因子(TOTP + Passkey),见《双因子认证》 |
DataProtection | 空 | 密钥环加密,见下文 |
MallSettings | 见商城文档 | 商城业务参数,见《商城模块》 |
TenantSettings | UseTenant=0 | 多租户,见《SaaS 多租户》 |
ModuleInit | 全部 false | --initdb 时初始化哪些模块 |
DbMigration | ReportOnly=false | 启动时补表/补列,见《数据迁移》 |
Quartz / QuartzTask | 见下文 | 定时任务,见《定时任务》 |
Upload | 见下文 | 上传与请求体限制,见《文件上传与对象存储》 |
OssProvider | s3 | 未配置时代码取 Aliyun;aliyun 分支尚未实现上传,实际请用 s3 |
S3 / ALIYUN_OSS | — | 对象存储凭据与访问域名 |
WechatPay | Enabled=false | 微信支付 V3,见下文 |
WxCorp | 空 | 企业微信通知,见《企业微信通知》 |
WxOpen | 空 | 公众号 AppID / AppSecret,见《微信公众号》 |
SmsOptions | Enabled=false | 短信;关闭时走模拟发送(只记日志) |
MailOptions | — | 发件邮箱数组,见《邮件发送》 |
Workflow | 站内信始终发 | 工作流邮件/短信分发开关 |
RedisServer | open=0 | 仅当 open=1 才初始化 Redis;dbCache=true 让 SqlSugar 查询缓存走 Redis,见《缓存切换》 |
CaptchaOptions | IgnoreCase=true | 验证码比较是否忽略大小写 |
OAuth | Enabled=false | 第三方登录,见《第三方授权登录》 |
InjectClass | 空 | 额外扫描 [AppService] 的插件程序集;ZR.* 已自动扫描 |
DemoMode | false | 演示模式(写操作受限) |
SingleLogin | false | true=单设备登录(新登录挤掉旧会话);跨节点需 Redis |
workId | 1 | 雪花 ID 机器号,两宿主必须不同 |
ShowDbLog | false | 是否打印 SQL 日志 |
sqlExecutionTime | 5 | SQL 超过该秒数记录告警并推送企业微信 |
Logging | — | 控制台日志级别;Microsoft.* 已默认静音 |
公网宿主额外节点
| 节点 | 默认 | 说明 |
|---|---|---|
MemberJwt | Expire=20160 | 会员令牌(mall-token)签发参数,仅本宿主使用 |
MockSms:Enabled | false | true 时验证码接口返回 devCode,生产必须 false |
MallSettings:AllowMockPay | false | WechatPay:Enabled=false 时是否允许模拟支付,生产必须 false |
公网宿主不注册后台鉴权管线,因此没有 JwtSettings 节;TenantSettings:ReservedSubDomains 必须包含商城域名(如 shop、front),否则会被当成租户子域。
四、易错项说明
1. dbConfigs 与多库
ConfigId是库的唯一标识:系统库固定"0",租户库必须与sys_tenant.TenantId严格一致;- 商城库/业务主库由顶层键
MallDb/BizMainDb指向某个ConfigId(业务主库兼容旧键MainDb);两者未配置时都回退系统库"0",因此单库部署只保留ConfigId="0"一项即可; - 库角色与
[Tenant]路由见《SaaS 多租户》。
2. DataProtection
CertificatePath 留空时:Windows 回退 DPAPI,Linux/Docker 明文落盘并在启动时告警。生产(尤其容器)建议配置证书,可用自签 PFX:
openssl req -x509 -newkey rsa:2048 -keyout dp.key -out dp.crt -days 3650 -nodes -subj "/CN=ZRAdmin-DataProtection"
openssl pkcs12 -export -in dp.crt -inkey dp.key -out dp.pfx -passout pass:你的口令
- 两个宿主的密钥环目录互相独立,可共用同一张证书;
- 证书丢失/更换会导致旧密钥无法解密(Cookie、防伪令牌失效),请与
DataProtection目录一并备份; - 多副本部署必须共享同一密钥环,见《后端部署》。
3. Quartz 与 QuartzTask
quartz.serializer.type写newtonsoft(对应Quartz.Serialization.Json包);binary依赖的BinaryFormatter已从 .NET 9+ 移除,内核对json也存在歧义,均不可用;- 默认是
RAMJobStore,只有配置jobStore.*持久化后序列化器才真正参与。需要持久化时补充(以 MySQL 为例):
"quartz.jobStore.type": "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
"quartz.jobStore.tablePrefix": "QRTZ_",
"quartz.jobStore.driverDelegateType": "Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz"
QuartzTask:Enabled默认false,不配置则不启动调度器、不从sys_tasks恢复任务;- 任务失败重试与通知默认关闭,避免升级后突然产生大量外部消息;通知按「任务 + 租户」在
NotifyCooldownMinutes内只发一次,防秒级任务刷屏; - 超时的语义:HTTP 走请求级超时并另留 5 秒收尾;SQL 与不协作取消的程序集任务超时后仍可能继续运行,结果标记为未知且不会自动重试。需要可控取消的任务用
CancellationToken(JobDataMap或Run(CancellationToken))。
4. ModuleInit 与 --initdb
ModuleInit 只在执行 --initdb 时生效,与运行期开关(TenantSettings:UseTenant、AiOptions:Enable)相互独立:
| 开关 | 初始化的内容 |
|---|---|
Saas | 平台菜单/权限/字典种子(无独立业务建表) |
Member | 会员表 ums_member(与商城同库)+ 自增种子校正 |
Mall | 商城建表 + 内置定时任务 + 菜单 |
Workflow | 工作流建表 + 内置定时任务 + 菜单 |
Pro | 专业版(AI)菜单与按钮权限 |
初始化幂等,重复执行只补缺。详见《快速开始》。
5. DbMigration
ReportOnly=true只输出差异报告、不执行 DDL,适合上线前预演;AdditionalTypes填完全限定类型名(如ZR.Model.YourEntity, ZR.Model),仅用于扩展自定义表;系统实体与各业务模块实体已自动注册;- 公网宿主已关闭自动迁移(
checkMigration:false);内网宿主多副本部署时应先由单实例完成迁移再扩容,避免并发 DDL 冲突。
6. Upload / S3 / ALIYUN_OSS
Upload.maxSize、Upload.requestLimitSize、S3.maxSize、ALIYUN_OSS.maxSize 的单位均为 MB(requestLimitSize 会再乘 1024 * 1024 后写入 Kestrel,填字节数会溢出为 0)。
Upload.maxSize目前代码未读取(预留项),实际生效的是requestLimitSize(整体上限)与各存储 Provider 的maxSize(单文件上限);uploadUrl是本地存储的资源访问前缀(默认后端地址),代码用new Uri(base, 对象路径)拼接,因此反向代理带路径前缀时必须以/结尾(如https://xxx.com/prod-api/);少了结尾/会把最后一段路径当文件名替换掉,链接会丢前缀;requestLimitSize同时写入FormOptions(MultipartBodyLengthLimit/MemoryBufferThreshold)与 Kestrel 的MaxRequestBodySize,缺失即为 0,会限制所有请求体;defaultStoreType只在调用方未显式指定storeType时生效,可选Local/ALIYUN/S3;想让所有业务上传走对象存储,改成S3即可;ALIYUN_OSS节点可保留可不留:AliyunOssProvider的上传未实现(恒返回 BadRequest),DeleteAsync虽返回 OK 但未真正删除且当前无调用方,实际请配OssProvider=s3;S3:domainUrl自建直连必须带桶名(如http://122.0.0.1:8000/zradmin),代码不会自动补;少了桶名会把对象键当根路径,打开返回AccessDenied。走 CDN/反代且已隐去桶名时才填去掉桶名的地址;留空则降级为相对路径;S3:forcePathStyle自建 S3 必须true,否则 SDK 按 Virtual-host 拼成bucket.ip:port,域名无法解析;S3:cacheControl随上传写入对象元数据,默认public, max-age=2592000(30 天),是省流量的关键项;留空则不设置,会覆盖同名对象时应缩短max-age;- 自建 S3 联调时把对象下载回来比对字节,不要只看上传返回 200。
7. WechatPay / MallSettings
| 键 | 说明 |
|---|---|
Enabled | 支付总开关;false 时会员支付只能走模拟通道 |
MerchantId / AppId | 商户号 mchid / 绑定的公众号或应用 AppId |
MerchantCertificateSerialNumber / MerchantCertificatePrivateKey | 商户 API 证书序列号与私钥(PEM,含 BEGIN PRIVATE KEY) |
MerchantV3Key | APIv3 密钥,用于回调解密 |
WechatPayPublicKeyId / WechatPayPublicKey | 微信支付公钥 ID(PUB_KEY_ID_xxx)与公钥(PEM,回调验签) |
MiniProgramAppId / MiniProgramSecret | 小程序 AppId/Secret;内网宿主租户续费走 JSAPI 用,不填则复用 AppId |
NotifyUrl | 支付结果回调,必须是公网 HTTPS 且指向公网宿主,路由不可改 |
MockNotifyUrl | 仅 Development 下 MockNotify 自调用,留空回退 NotifyUrl |
H5ReturnUrl | 商城 H5 支付回跳地址;租户续费的 H5 通道已停用 |
- 商城下单与支付回迁到公网宿主;内网宿主保留该配置用于租户续费下单,两宿主须使用同一商户号;
WechatPay:Enabled=false时,会员支付必须显式开MallSettings:AllowMockPay=true才走模拟支付;Enabled=true但商户配置缺失会直接拒绝支付,不会降级为模拟支付。
8. TenantSettings
UseTenant:0关闭,1/true/on启用,两个宿主必须一致;RootDomains:按子域解析租户时据此剥离标签(a.zradmin.net→a),localhost不受影响;ReservedSubDomains:这些子域不解析为租户,缺省www/admin/api/doc,公网商城的域名/子域必须列入;PlatformMenuPermPrefixes:平台专属权限前缀(前缀匹配),非主租户不可见、不可访问;新增平台级功能时把权限前缀加进来即可,无需改代码。
9. TwoFactor / Passkey
- 全节点可缺省,缺省即只提供 TOTP;
- WebAuthn 必须同时满足:
WebAuthnEnabled=true、WebAuthnOrigins已配置(含协议)、站点为 HTTPS 或 localhost,否则该方式整体关闭(fail-closed); WebAuthnRpId留空取Origins首项域名,多个子域共用时建议配根域名;- 浏览器不允许内网 IP + HTTP 使用 Passkey。
10. OAuth
OAuth 节未列出的字段取代码默认值:StateMinutes=10、AutoCreate=0(只绑定不建号)、FrontendCallbackPath=/sociallogin;多租户用 渠道@租户 覆盖。完整字段与回调地址规则见《第三方授权登录》。
11. 其它
workId:两个宿主必须不同,否则会生成重复雪花 ID;RedisServer:open:1才启用 Redis,否则回退内存缓存;单设备登录会话、OAuth state 需要跨节点一致时必须启用(接口限流用进程内 MemoryCache,不依赖 Redis);ShowDbLog=true会打印完整 SQL,仅在排障时临时开启;SingleLogin:true为单设备登录(新登录挤掉旧会话),false允许多设备/浏览器同时登录。
五、环境变量示例
# 后台令牌(≥32 字符,与公网宿主不同)
JwtSettings__SecretKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 会员令牌(≥32 字节,仅公网宿主)
MemberJwt__SecretKey=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# 数据库连接串整体覆盖
dbConfigs__0__Conn="Data Source=10.0.0.8;Initial Catalog=ZrAdmin;User ID=sa;Password=***;"
# 定时任务开关
QuartzTask__Enabled=true
注意
环境变量里的 __ 是层级分隔符;用 appsettings.{Environment}.json 覆盖时注意数组按下标覆盖(dbConfigs[1] 对应 dbConfigs__1__xxx)。
