簡體   English   中英

ASP核心:Azure廣告驗證+自定義JWT +自定義身份存儲

[英]Asp Core: Azure Ad Auth + custom JWT + custom Identity store

使用ASP.NET Core 2.0,我嘗試實現以下目標:

  1. 通過Azure AD(注冊的應用程序)進行身份驗證
  2. 自定義JWT作為身份驗證方案
    • 使Web應用程序身份驗證可跨服務器/實例工作
    • 能夠保存承載以登錄桌面客戶端
  3. 有一個自定義標識存儲,以介紹自定義角色,策略等。

所有這些部分都有有效的示例,但是在嘗試將它們組合時,我偶然發現了一些問題。

Web Api + Azure Ad Auth示例使用JWT令牌進行身份驗證,但沒有用於驗證或創建令牌的邏輯。 它也沒有登錄/注銷的邏輯,但這似乎是合理的,只是Api。

這是Web Api示例代碼的快速提醒:

AzureAdAuthenticationBuilderExtensions.cs

using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Authentication
{
    public static class AzureAdServiceCollectionExtensions
    {
        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder)
            => builder.AddAzureAdBearer(_ => { });

        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
        {
            builder.Services.Configure(configureOptions);
            builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureAzureOptions>();
            builder.AddJwtBearer();
            return builder;
        }

        private class ConfigureAzureOptions: IConfigureNamedOptions<JwtBearerOptions>
        {
            private readonly AzureAdOptions _azureOptions;

            public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }

            public void Configure(string name, JwtBearerOptions options)
            {
                options.Audience = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
            }

            public void Configure(JwtBearerOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }
}

Startup.cs的摘錄

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

    services.AddMvc();
}

另一方面,Web應用程序+ Azure廣告示例將OpenId與cookie結合使用,並且具有登錄/注銷邏輯:

AzureAdAuthenticationBuilderExtensions.cs

using System;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Authentication
{
    public static class AzureAdAuthenticationBuilderExtensions
    {
        public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
            => builder.AddAzureAd(_ => { });

        public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
        {
            builder.Services.Configure(configureOptions);
            builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
            builder.AddOpenIdConnect();
            return builder;
        }

        private class ConfigureAzureOptions : IConfigureNamedOptions<OpenIdConnectOptions>
        {
            private readonly AzureAdOptions _azureOptions;

            public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }

            public void Configure(string name, OpenIdConnectOptions options)
            {
                options.ClientId = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
                options.UseTokenLifetime = true;
                options.CallbackPath = _azureOptions.CallbackPath;
                options.RequireHttpsMetadata = false;
            }

            public void Configure(OpenIdConnectOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }
}

Startup.cs的摘錄

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddAzureAd(options => Configuration.Bind("AzureAd", options))
    .AddCookie();

    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    })
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.AllowAnonymousToFolder("/Account");
    });
}

AccountController.cs

public class AccountController : Controller
{
    [HttpGet]
    public IActionResult SignIn()
    {
        var redirectUrl = Url.Page("/Index");
        return Challenge(
            new AuthenticationProperties { RedirectUri = redirectUrl },
            OpenIdConnectDefaults.AuthenticationScheme
        );
    }

    [HttpGet]
    public IActionResult SignOut()
    {
        var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme);
        return SignOut(
            new AuthenticationProperties { RedirectUri = callbackUrl },
            CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme
        );
    }
}

我以某種方式合並了這兩個變體,但顯然不起作用。 我在登錄方法JwtBearerDefaults當然替換為CookieAuthenticationDefault

Startup.cs的摘錄

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFrameworkSqlServer().AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddAzureAd(options => Configuration.Bind("AzureAd", options))
    .AddJwtBearer(options =>
    {
        options.IncludeErrorDetails = true;

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "localhost",
            ValidAudience = "localhost",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("test"))
        };
    });

    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    })
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.AllowAnonymousToFolder("/Account");
    });
}

我不完全了解不同的身份驗證如何鏈接或相互依賴。 我了解,OpenId在內部使用某種JWT,仍然存在以下問題:

  • 為什么Web Api示例僅使用JWT,而其他示例使用帶有Cookie的OpenId?
  • 為什么OpenId示例首先不使用JWT?
  • 自定義JWT是否可以與OpenId一起使用?
  • 是否可以引入自定義身份存儲,但保留Azure AD進行登錄(和登錄名)?

如果您能為我提供一些指導,那就太好了,而無需完整的工作示例(即使這很好)

如果要在ASP.NET Core Web應用程序中結合使用Cookies和Bearer授權,則可以遵循以下代碼段:

services.AddAuthentication(sharedOptions =>
{
    sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions=> {
    jwtOptions.IncludeErrorDetails = true;
    jwtOptions.Authority = "{Authority}";
    jwtOptions.Audience = "{Audience}";
    jwtOptions.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidIssuer = "{ValidIssuer}",
        ValidAudience = "{ValidAudience}"
    };
    jwtOptions.Events = new JwtBearerEvents()
    {
        OnAuthenticationFailed = context => {
            //TODO:
            return Task.FromResult(0);
        },
        OnTokenValidated = context => {
            //At this point, the security token has been validated successfully and a ClaimsIdentity has been created
            var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
            //add your custom claims here
            claimsIdentity.AddClaim(new Claim("test", "helloworld!!!"));

            return Task.FromResult(0);
        }
    };
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();

我注意到您添加了一個全局AuthorizeFilter ,這時您需要確保匿名動作需要使用AllowAnonymous屬性進行修飾。

services.AddMvc(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme)
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
})

或者,您可以使用Authorize屬性裝飾控制器動作,如下所示:

[Authorize(AuthenticationSchemes = "Cookies,Bearer")]
public IActionResult UserInfo()
{
    return Json(User.Claims.Select(c => new { key = c.Type, value = c.Value }));
}

對於OpenID Connect中間件,您可以修改AzureAdAuthenticationBuilderExtensions.cs文件下的Configure(string name, OpenIdConnectOptions options)方法,以添加自定義聲明(例如,角色等),如下所示:

public void Configure(string name, OpenIdConnectOptions options)
{
    options.ClientId = _azureOptions.ClientId;
    options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
    options.UseTokenLifetime = true;
    options.CallbackPath = _azureOptions.CallbackPath;
    options.RequireHttpsMetadata = false;
    //the new code
    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = context =>
        {   
            var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
            //add your custom claims here
            claimsIdentity.AddClaim(new Claim("test", "helloworld!!!"));

            return Task.FromResult(0);
        }
    };
}

總而言之,在配置后,您可以結合使用Cookies和Bearer身份驗證,並且在驗證令牌之后,您可以檢索用戶標識符並從數據庫中獲取其他用戶信息,然后將它們附加到ClaimsIdentity

對於桌面客戶端,您可以利用ADAL (對於AD應用程序v1.0)或MSAL (對於AD應用程序v2.0)登錄並檢索access_tokenid_token ,然后將其用作承載令牌來訪問您的Web應用程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM