RT.MesWeb.Authentication
1.2.0
dotnet add package RT.MesWeb.Authentication --version 1.2.0
NuGet\Install-Package RT.MesWeb.Authentication -Version 1.2.0
<PackageReference Include="RT.MesWeb.Authentication" Version="1.2.0" />
<PackageVersion Include="RT.MesWeb.Authentication" Version="1.2.0" />
<PackageReference Include="RT.MesWeb.Authentication" />
paket add RT.MesWeb.Authentication --version 1.2.0
#r "nuget: RT.MesWeb.Authentication, 1.2.0"
#:package RT.MesWeb.Authentication@1.2.0
#addin nuget:?package=RT.MesWeb.Authentication&version=1.2.0
#tool nuget:?package=RT.MesWeb.Authentication&version=1.2.0
Authentication 使用说明
安装
dotnet add package RT.MesWeb.Authentication
目标框架:net8.0 / net10.0
快速开始
- 引用模块:启动模块标注
[DependsOn(typeof(AuthenticationApiModule))]。 - 配置
appsettings.json:至少填Authentication:AuthEntities:ConnectionString(唯一必填项)。 - 注册服务(构建前):调用
services.RegisterModules(configuration)——AuthenticationApiModule会自动完成IHttpContextAccessor、JwtOptions/AuthenticationOptions、AuthEntitiesDbContext、JWT Bearer 认证及各服务的注册;多语言仍需在主工程单独注册。 - 启动授权服务:
MesLicenseChecker.Current.Start(...)。 - 可选:注册
ILoginProvider扩展登录程序。
完整代码片段见下文「使用说明」。
配置要求
- 需要在
appsettings.json中添加以下配置: - 必填:仅
Authentication:AuthEntities:ConnectionString一项(无默认值)。其余配置均有默认值,可按需覆盖。 - 安全提示:
SystemAccount.DefaultPassword/SystemAccount.DefaultAdminPassword为内置的已知默认口令,生产环境务必通过配置覆盖。
{
"Authentication": {
"OAuth": {
"Enabled": true,
"AccessTokenMinutes": 15,
"DefaultClientId": "mes-web",
"Scopes": [ "mes.api", "offline_access" ],
"RefreshToken": {
"SlidingMinutes": 720,
"AbsoluteMinutes": 720,
"IdleMinutes": 30,
"RotationGraceSeconds": 10
},
"AccessTokenRevocation": {
"Enabled": false
},
"TokenCleanup": {
"Enabled": true,
"RetentionDays": 7,
"IntervalMinutes": 60,
"BatchSize": 500
}
},
"AuthEntities": {
"ConnectionString": "Server=127.0.0.1;uid=sa;pwd=password;database=dbname;Trust Server Certificate=true;",
"DatabaseType": "SqlServer" // 可选(默认 SqlServer),可选值:SqlServer / Oracle / DM / MySql / PostgreSql(大小写不敏感;非法值启动即报错)
},
"SystemAccount": {
"AdminRoleName": "administrator",
"AdminUserName": "admin",
"DefaultPassword": "mes123",
"DefaultAdminPassword": "aspnet2025"
},
"ThreeManager": {
"EnableThreeManager": false,
"SystemManagerUserName": "110101199001010001",
"SecurityManagerUserName": "110101199001010002",
"SecurityAuditorUserName": "110101199001010003",
"SystemManagerRoleName": "系统管理员",
"SecurityManagerRoleName": "安全管理员",
"SecurityAuditorRoleName": "安全审计员",
"SystemManagerPermissions": "",
"SecurityManagerPermissions": "",
"SecurityAuditorPermissions": ""
},
"AccessControl": {
"AllowMultiTerminal": false,
"SessionTimeoutSeconds": 200,
"HeartbeatIntervalSeconds": 60,
"Lockout": {
"MaxFailedAccessAttempts": 5,
"DefaultLockoutMinutes": 30,
"InactiveDaysToLock": 90
},
"PasswordPolicy": {
"ExpiryDays": 180,
"ReminderDaysBeforeExpiry": 7,
"RequiredLength": 8,
"ComplexityRegex": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$",
"AllowRepeatWithLast": false
}
}
}
}
数据库要求
- AuthEntities指向的数据库应当全套权限认证体系的数据表
- 1.2.0 新增:必须存在
AUTH_TOKEN表(OAuth2 令牌,仅存 SHA-256 哈希)。建表脚本位于Scripts/OAuth2/:01_postgresql.sql/02_sqlserver.sql/03_mysql.sql/04_oracle.sql/05_dm.sql,按实际数据库执行一次即可。表上的USER_NO/APP_NO外键仅作完整性约束、不级联,删除用户时由应用层先清理其令牌行;执行脚本前AUTH_USER、AUTH_APPLICATION必须已存在。 - AuthEntities指向的数据库应当包含以下序列:
- AUTH_SEQ_NO:用于生成数据表编号的序列。
- 数据库中应包含序号生成机制
- SQL Server:应包含生成Sequence的存储过程和Sequence序列对象
- Oracle:包含生成Sequence的序列对象即可
- DM:包含生成Sequence的序列对象即可
- MySQL:应包含序列表
sequence(NAME/CURRENT_VALUE/INCREMENT)、函数CurrVal/NextVal/SetVal及存储过程aic_sp_get_next_sequence_value - PostgreSQL:包含生成Sequence的序列对象即可
使用说明
- Authentication服务配置:
// 在MesWebApiHostModule中增加对AuthenticationApiModule的依赖:
[DependsOn(
typeof(AuthenticationApiModule)
)]
- 在主工程调用
services.RegisterModules(configuration),AuthenticationApiModule会自动完成以下注册:IHttpContextAccessorJwtOptions/AuthenticationOptions配置绑定AuthEntitiesDbContext(按DatabaseType选择 SqlServer / Oracle / DM / MySql / PostgreSql)- JWT Bearer 认证
- 登录 / 账户 / 会话等服务
- 若不使用模块系统,也可单独调用
services.ConfigureAuthentication(configuration)完成上述注册(非模块化场景): - LoginService需要在主工程内注册扩展登录提供程序,每个提供程序实现
ILoginProvider并声明LoginType,直接注册为ILoginProvider即可:
services.AddTransient<ILoginProvider, CustomLoginProvider>();
当前项目依赖Redis服务(Redis服务内容参见RT.MesWeb.Redis);
登录会签发 JWT,
Jwt配置节见 RT.MesWeb.Framework 中的 README(SecurityKey内置默认密钥,未配置也可用)。
OAuth2 令牌接口(1.2.0 起)
登录改为 OAuth2 双令牌:AccessToken(默认 15 分钟)+ RefreshToken(默认 12 小时,闲置 30 分钟失效)。
令牌端点的成功与失败都返回统一响应 ApiResult:HTTP 恒为 200,业务状态在 StatusCode,失败说明在 Message。
端点
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/authorize/token |
令牌端点,支持 password 与 refresh_token 两种 grant;仅接受 JSON,字段为 PascalCase |
| POST | /api/authorize/revoke |
令牌撤销,无论令牌是否存在均返回成功 |
| POST | /api/authorize/login |
旧登录接口,仅兼容存量前端、计划停用;新前端请用 /api/authorize/token |
| POST | /api/authorize/logout |
旧登出接口,清理会话并撤销该会话的 refresh_token |
password grant
curl -X POST http://localhost:5144/api/authorize/token \
-H "Content-Type: application/json" \
-d '{"GrantType":"password","Username":"admin","Password":"***","AppId":"mesapp","LoginType":"BS01","DeviceId":"PC-01","ClientId":"mes-web"}'
成功响应:
{
"Value": {
"AccessToken": "eyJhbGciOiJIUzI1NiIs...",
"TokenType": "Bearer",
"ExpiresIn": 900,
"RefreshToken": "0f3b9c...",
"Scope": "mes.api offline_access",
"SessionId": "7d2f1c8a9b6e4f0d8c3a5b7e9d1f2a4c",
"IdleTimeout": 1800,
"HeartbeatInterval": 60,
"AppId": "mesapp",
"UserId": "admin",
"UserName": "管理员",
"IsAdmin": true,
"PasswordExpireDays": 172,
"PasswordExpiringSoon": false
},
"StatusCode": 200,
"Message": ""
}
AppId ~ PasswordExpiringSoon 为用户资料,password 与 refresh_token 两种 grant 都会返回(刷新时按令牌关联用户实时查询)。
失败响应示例:
{ "Value": null, "StatusCode": 403, "Message": "无效的登录请求,用户名不存在!" }
StatusCode 约定:400 请求参数错误、401 刷新令牌失效或客户端无效、403 账号或口令错误、409 多端冲突(Message 为 session_conflict,客户端确认后以 force_login=true 重试)、500 服务端异常。
refresh_token grant
curl -X POST http://localhost:5144/api/authorize/token \
-H "Content-Type: application/json" \
-d '{"GrantType":"refresh_token","RefreshToken":"<当前 RefreshToken>","ClientId":"mes-web"}'
- 每次刷新都会轮换:旧 refresh_token 立即失效并返回新的 refresh_token,客户端必须保存最新的一个。
- 前端需单飞(single-flight)+ 跨标签页广播(如
BroadcastChannel),避免并发刷新。 - 旧令牌在
RotationGraceSeconds窗口内重复使用只会被拒绝;超出窗口后再次使用视为令牌泄露,整个令牌族(family)被撤销。
失效规则
- 登出、踢人:撤销该会话的 refresh_token。
- 改密、重置密码、锁定、删除用户:撤销该用户在该应用下的全部 refresh_token。
- 强制登录挤掉旧会话:撤销该用户在该应用下的历史 refresh_token。
- 超过
IdleMinutes未刷新:拒绝刷新并撤销该令牌。 - 用户被锁定/删除、密码版本(
PWD_SET_TIME)变化:刷新时拒绝并撤销令牌族。 access_token无状态,失效依赖自身过期;拒绝列表通过OAuth:AccessTokenRevocation:Enabled预留(默认关闭)。
令牌表维护
AUTH_TOKEN 只追加不覆盖(轮换、登出都会留下历史行),库内自带 TokenCleanupService 后台任务:
- 每
IntervalMinutes(默认 60)执行一次,分批删除CUR_EXPIRE_TIME早于「当前时间 −RetentionDays」的行; - 默认保留 7 天,便于排查问题;
RetentionDays=0表示过期即删; - 按
BatchSize(默认 500)分批删除,避免一次性删除造成长事务与锁等待; - 多个实例同时运行是安全的(删除幂等),
Enabled=false可关闭。
在线状态(1.2.0 起)
带 Bearer 的业务请求执行成功后,SessionActivityFilter 会自动触达一次在线会话;同一会话 60 秒内最多触达一次,因此前端无需再定时调用 /api/authorize/heartbeat(接口仍保留,兼容旧前端)。
- 触达只影响在线状态展示与多端登录冲突检测,不改变 refresh_token 寿命;
- 请求失败(抛异常或 HTTP ≥ 400)不触达;会话已被踢下线时不重建;
- 部署注意:
Authentication:AccessControl:SessionTimeoutSeconds应不小于触达间隔的 2~3 倍(默认 200s vs 60s,安全)。
改造说明
认证接口契约、前端对接流程与遗留任务见源码仓库中的 认证接口说明.md(面向前端开发,只描述当前体系;该文档不随 NuGet 包发布)。
- Authentication有授权检查,需要在主工程内启动授权服务(授权配置参见RT.MesWeb.Framework中的README):
MesLicenseChecker.Current.Start(configuration, hostEnvironment.ContentRootPath, logger);
- 当前项目已经启用多语言支持,需要在主工程内注册多语言资源:
// 在应用程序构建前:
services.AddLocalization(options => options.ResourcesPath = "");
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("zh-CN"), // 中文简体
new CultureInfo("en-US"), // 英文
};
// 设置支持的语言列表
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
// 默认语言(无语言标识时使用)
options.DefaultRequestCulture = new RequestCulture("zh-CN");
// 优先级:URL参数 > Cookie > 请求头
options.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider());
});
// 在应用程序构建后:
app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Audit.Mvc.Core (>= 30.0.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.26)
- RT.MesWeb.Audit (>= 1.2.0)
- RT.MesWeb.DataProviders (>= 1.2.0)
- RT.MesWeb.Framework (>= 1.2.0)
- RT.MesWeb.Redis (>= 1.2.0)
-
net8.0
- Audit.Mvc.Core (>= 30.0.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.26)
- RT.MesWeb.Audit (>= 1.2.0)
- RT.MesWeb.DataProviders (>= 1.2.0)
- RT.MesWeb.Framework (>= 1.2.0)
- RT.MesWeb.Redis (>= 1.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.