agilelabs-fx-docs main tasks/integrate-aspnet-authentication.md

接入 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 还有其他宿主级开关,完整清单见 应用功能开关

接入步骤

  1. 注册项目真实认证方案。
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))
        };
    });
  1. 在 AgileLabs WebApp 宿主配置中开启集成开关。
await AgileLabApplication.StartApplicationAsync<DefaultMvcApplicationOptions>(options =>
{
    options.ConfigureHostBuilder += (IHostBuilder hostBuilder, AppBuildContext context) =>
    {
        context.FeatureSwitch.IsIntegrateAspNetAuthentication = true;
    };
});
  1. 在需要保护的接口上标注 [Authorize]
[Authorize]
[ApiController]
[Route("api/admin/projects")]
public class AdminProjectController : ControllerBase
{
    [HttpGet]
    public IReadOnlyList<ProjectDto> GetProjects()
    {
        ...
    }
}
  1. 对开放接口显式标注匿名访问。
[AllowAnonymous]
[HttpPost("login")]
public LoginResultDto Login(LoginRequestDto request)
{
    ...
}
  1. 验证匿名请求和已认证请求。
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 或接口文档已经说明受保护接口的认证要求。

相关页面