接入 ASP.NET Core 认证授权
本页指导其他项目在 AgileLabs WebApp 宿主中引入 ASP.NET Core 认证授权。核心做法是:项目继续按 ASP.NET Core 标准方式注册具体认证方案,框架通过 IsIntegrateAspNetAuthentication 统一接管认证授权中间件的管道挂接。
适用场景
- 项目使用 AgileLabs WebApp 宿主启动。
- Controller、Action 或 Endpoint 上使用
[Authorize]。 - 希望认证授权中间件由框架统一挂在正确位置。
- 希望鉴权失败、禁止访问等结果继续映射到统一 API 封包。
如果项目没有使用 AgileLabs WebApp 宿主,就按普通 ASP.NET Core 项目的方式手动注册 UseAuthentication() 和 UseAuthorization()。
设计作用
IsIntegrateAspNetAuthentication 是框架接入 ASP.NET Core 认证授权的集成开关。它解决的是“认证授权中间件由谁挂、挂在哪里、授权失败如何进入统一封包”。
它负责:
- 在服务注册阶段补齐 ASP.NET Core 授权服务。
- 注册默认
IAuthorizationMiddlewareResultHandler,让 challenge/forbid 可以映射到 API 统一封包。 - 在请求管道中把
UseAuthentication()和UseAuthorization()插入到UseRouting()之后、UseEndpoints()之前。
它不负责:
- 用户登录流程。
- JWT 签发。
- Cookie 登录。
- 具体 authentication scheme。
- 角色、策略、菜单权限或资源级授权模型。
- token 黑名单、刷新 token 或服务端会话撤销。
因此,开关开启后,项目仍然必须自行注册认证方案,例如 JWT Bearer、Cookie、Header handler 或其他自定义 handler。
源码依据
当前实现可从三个入口理解:
| 源码入口 | 作用 |
|---|---|
AgileLabs.WebApp/WebApp/Hosting/AgileLabsApplicationFeatureSwitch.cs |
定义 IsIntegrateAspNetAuthentication,默认值为 false |
AgileLabs.WebApp/Securities/SecurityServiceRegister.cs |
开关开启时注册 AddAuthorization() 和默认授权结果处理器 |
AgileLabs.WebApp/AgileLabContext.cs |
开关开启时在 UseRouting() 后调用 UseAuthentication() 和 UseAuthorization() |
这个开关不是替代 ASP.NET Core 认证系统,而是把 ASP.NET Core 认证授权系统纳入 AgileLabs WebApp 的统一装配链。
AgileLabsApplicationFeatureSwitch 还有其他宿主级开关,完整清单见 应用功能开关。
接入步骤
- 注册项目真实认证方案。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = tokenSettings.Issuer,
ValidAudience = tokenSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSettings.SigningKey))
};
});
- 在 AgileLabs WebApp 宿主配置中开启集成开关。
await AgileLabApplication.StartApplicationAsync<DefaultMvcApplicationOptions>(options =>
{
options.ConfigureHostBuilder += (IHostBuilder hostBuilder, AppBuildContext context) =>
{
context.FeatureSwitch.IsIntegrateAspNetAuthentication = true;
};
});
- 在需要保护的接口上标注
[Authorize]。
[Authorize]
[ApiController]
[Route("api/admin/projects")]
public class AdminProjectController : ControllerBase
{
[HttpGet]
public IReadOnlyList<ProjectDto> GetProjects()
{
...
}
}
- 对开放接口显式标注匿名访问。
[AllowAnonymous]
[HttpPost("login")]
public LoginResultDto Login(LoginRequestDto request)
{
...
}
- 验证匿名请求和已认证请求。
curl -i http://localhost:5000/api/admin/projects
curl -i \
-H "Authorization: Bearer <token>" \
http://localhost:5000/api/admin/projects
未认证请求应进入统一错误封包;携带有效凭证后,接口应正常返回业务数据。
JWT Bearer 最小示例
JWT Bearer 项目通常需要同时完成两件事:
- 登录接口负责校验账号、AccessCode 或其他凭证,并签发 JWT。
- API 宿主负责注册 JWT Bearer 校验参数,并开启
IsIntegrateAspNetAuthentication。
示例结构如下:
public class ServiceRegister : IServiceRegister
{
public void ConfigureServices(IServiceCollection services, AppBuildContext buildContext)
{
var tokenSettings = buildContext.Configuration
.GetSection("TokenSettings")
.Get<TokenSettings>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = tokenSettings.Issuer,
ValidAudience = tokenSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(tokenSettings.SigningKey))
};
});
}
}
await AgileLabApplication.StartApplicationAsync<DefaultMvcApplicationOptions>(options =>
{
options.ConfigureHostBuilder += (IHostBuilder hostBuilder, AppBuildContext context) =>
{
context.FeatureSwitch.IsIntegrateAspNetAuthentication = true;
};
});
这里的开关只负责让 [Authorize] 真正经过 ASP.NET Core 认证授权中间件。JWT 如何签发、token 包含哪些 claims、过期时间如何设置,仍由项目自己的登录方案决定。
自定义认证方案
如果项目不用 JWT,也可以继续使用 ASP.NET Core 标准认证扩展。
Header handler 示例:
services.AddAuthentication(HeaderTokenAuthHandler.SchemeName)
.AddScheme<AuthenticationSchemeOptions, HeaderTokenAuthHandler>(
HeaderTokenAuthHandler.SchemeName,
_ => { });
Cookie 示例:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/account/login";
options.AccessDeniedPath = "/account/denied";
});
无论使用哪种 scheme,AgileLabs 集成开关的职责都不变:项目注册认证方案,框架统一挂接认证授权中间件。
授权失败返回
开启 IsIntegrateAspNetAuthentication 后,框架会默认注册 ApiEnvelopeAuthorizationMiddlewareResultHandler。它的目标是让 ASP.NET Core 授权中间件产生的 challenge/forbid 结果进入统一 API 错误处理口径,而不是直接暴露成一套独立的响应结构。
项目需要重点确认:
- 未登录访问受保护接口时,前端能拿到统一封包中的鉴权失败错误码。
- 已登录但权限不足时,前端能拿到统一封包中的禁止访问错误码。
- 前端不要同时兼容多套鉴权失败响应格式。
具体错误码应以项目级 IApiExceptionProcessor、授权结果处理器和前端契约约定为准。
常见错误
| 错误做法 | 结果 | 正确做法 |
|---|---|---|
只开启 IsIntegrateAspNetAuthentication,没有注册 authentication scheme |
[Authorize] 无法完成身份识别 |
先注册 AddAuthentication(...).AddJwtBearer(...) 或其他 scheme |
开启开关后又手动写 UseAuthentication() / UseAuthorization() |
管道重复接入,顺序和行为难排查 | 让框架统一挂接 |
| 只注册认证,不确认授权服务和授权结果处理 | challenge/forbid 可能绕开统一封包 | 使用框架集成开关接入 |
| 把开关当成登录方案 | 没有 token 签发、账号校验或会话管理 | 登录流程由项目自己实现 |
Controller 写了 [Authorize],但宿主没有任何认证授权管道 |
特性不会自动生效 | 开启框架集成或手动完整接入 |
引入检查清单
- 已注册真实认证方案,例如 JWT Bearer、Cookie 或 Header handler。
- 已开启
context.FeatureSwitch.IsIntegrateAspNetAuthentication = true。 - 宿主中没有重复手写
UseAuthentication()和UseAuthorization()。 - 受保护接口已标注
[Authorize]。 - 登录、健康检查、回调等开放接口已显式标注
[AllowAnonymous]或有等价匿名策略。 - 未登录、token 失效、权限不足三类请求都验证过。
- 鉴权失败响应与前端统一封包契约一致。
- Swagger 或接口文档已经说明受保护接口的认证要求。