实施 AbpIdentity cookie 和 JwtBearer

Implementing AbpIdentity cookies and JwtBearer

我继承了一个同时使用 Web API 和 MVC 前端的 ASPnetzero 应用程序。 API 通过 Bearer 进行身份验证,前端通过 AbpIdentity (Cookies) 进行身份验证。几天前,我勇敢地决定更新我的 nuGet 包。此更新附带从 .netCore v1 到 v2 的升级。但是在 JwtBearer 中间件过时后,我在身份验证方面遇到了一些困难。我可以使用 cookie 进行身份验证,但不能使用 Bearer Tokens。

我几乎什么都试过了。使用多种身份验证方法意味着一次只能使用一种方法。

在 Startup.cs 中,我有以下(片段):

public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddAbpIdentity<Tenant, User, Role>()
            .AddUserManager<UserManager>()
            .AddRoleManager<RoleManager>()
            .AddSignInManager<SignInManager>()
            .AddClaimsPrincipalFactory<UserClaimsPrincipalFactory>()
            .AddDefaultTokenProviders();
        }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
          AuthConfigurer.Configure(app, _appConfiguration);
         }

然而,这是一个自我回答的问题,我希望我能帮助任何有相似或相同情况的人,因为我已经整理了我自己的解决方案。这个想法是让应用程序使用 Bearer 令牌(仅在使用 API 时)和 cookie(仅在使用 MVC 时)。

我也遇到了挑战,因为 MVC 对 API 进行 XHR 调用以获取要在前端显示的数据。这意味着 API 还需要使用 Cookie(但仅适用于 MVC 用户)。

所以我终于弄明白了,它需要相当多的改造。结果是:

  1. API 用户仅使用 Bearer Token 进行身份验证
  2. MVC 用户使用 Cookie 进行身份验证,登录后应用程序中的 API 调用使用相同的身份验证。

所有更改都在 Startup.cs 中进行,我还注释掉了对 AuthConfigure.cs 文件的引用,该文件现已过时。我愿意接受对解决方案的任何改进或建议。

Startup.cs 文件中的重要部分:

public IServiceProvider ConfigureServices(IServiceCollection services)
        {
          services.AddAuthentication()
                .AddJwtBearer(cfg =>
                {
                    cfg.RequireHttpsMetadata = false;
                    cfg.SaveToken = true;

                    cfg.TokenValidationParameters = new TokenValidationParameters()
                    {
                        // The application URL is used for the Issuer and Audience and is included in the appsettings.json
                        ValidIssuer = _appConfiguration["Authentication:JwtBearer:Issuer"],
                        ValidAudience = _appConfiguration["Authentication:JwtBearer:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appConfiguration["Authentication:JwtBearer:SecurityKey"]))
                    };

                });

             // Activate Cookie Authentication without Identity, since Abp already implements Identity below.
             services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Login");

             // Add the Authentication Scheme Provider which will set the authentication method based on the kind of request. i.e API or MVC
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<IAuthenticationSchemeProvider, CustomAuthenticationSchemeProvider>();

             // Some of these extensions changed
             services.AddAbpIdentity<Tenant, User, Role>()
            .AddUserManager<UserManager>()
            .AddRoleManager<RoleManager>()
            .AddSignInManager<SignInManager>()
            .AddClaimsPrincipalFactory<UserClaimsPrincipalFactory>()
            .AddDefaultTokenProviders();
//…
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
// app.UseAuthentication is critical here
            app.UseAuthentication();
            app.UseAbp(); //Initializes ABP framework.
            app.UseCors("CorsPolicy");
//…
 //AuthConfigurer.Configure(app, _appConfiguration);
//…
         }
}
public class CustomAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
    private readonly IHttpContextAccessor httpContextAccessor;

    public CustomAuthenticationSchemeProvider(
        IHttpContextAccessor httpContextAccessor,
        IOptions<AuthenticationOptions> options)
        : base(options)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    private async Task<AuthenticationScheme> GetRequestSchemeAsync()
    {
        var request = httpContextAccessor.HttpContext?.Request;
        if (request == null)
        {
            throw new ArgumentNullException("The HTTP request cannot be retrieved.");
        }

        // For API requests, use authentication tokens.

        var authHeader = httpContextAccessor.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
        if (authHeader?.StartsWith("Bearer ") == true)
        {
            return await GetSchemeAsync(JwtBearerDefaults.AuthenticationScheme);
        }

        // For the other requests, return null to let the base methods
        // decide what's the best scheme based on the default schemes
        // configured in the global authentication options.
        return null;
    }

    public override async Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultAuthenticateSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultChallengeSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultForbidSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultForbidSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultSignInSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultSignInSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultSignOutSchemeAsync();
}