Asp Core:Azure 广告身份验证 + 自定义 JWT + 自定义身份存储

2024-01-27

使用 ASP.NET Core 2.0,我尝试实现以下目标:

  1. 通过 Azure AD 进行身份验证(注册应用程序)
  2. Custom JWT as the authentication scheme to
    • 使 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);
            }
        }
    }
}

摘录启动.cs

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

    services.AddMvc();
}

另一方面,Web 应用程序 + Azure Ad 示例使用 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);
            }
        }
    }
}

摘录启动.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
        );
    }
}

我已经以某种方式合并了这两个变体,但显然它不起作用。我当然替换了CookieAuthenticationDefault with JwtBearerDefaults在登录方法中。

摘录启动.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,而另一个示例则使用 OpenId 和 cookies ?
  • 为什么 OpenId 示例首先不使用 JWT ?
  • 自定义 JWT 可以与 OpenId 一起使用吗?
  • 是否可以引入自定义身份存储,但保留用于登录的 Azure AD(以及登录名)?

如果您能为我提供一些指导,那就太好了,不需要完整的工作示例(尽管这会很棒)


如果您想在 ASP.NET Core Web 应用程序中结合 Cookie 和 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 中间件,您可以修改Configure(string name, OpenIdConnectOptions options)下的方法AzureAdAuthenticationBuilderExtensions.cs文件来添加您的自定义声明(例如角色等),如下所示:

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);
        }
    };
}

总之,在配置时,您可以将 Cookie 和 Bearer 身份验证结合起来,在验证令牌后,您可以检索用户标识符并从数据库中获取其他用户信息,然后将它们附加到ClaimsIdentity.

对于桌面客户端,您可以利用ADAL https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries(适用于 AD 应用程序 v1.0)或MSAL https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-libraries(适用于 AD 应用程序 v2.0)登录并检索access_token or id_token,然后使用它作为不记名令牌来访问您的 Web 应用程序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Asp Core:Azure 广告身份验证 + 自定义 JWT + 自定义身份存储 的相关文章

随机推荐

  • MySQL - 选择然后更新

    我有一个用 PHP 编写的脚本 其中有这一行可以正常工作以选择我需要的数据 result mysql query SELECT product name sku qty FROM supplier dropship items WHERE
  • iOS 屏幕截图部分

    我有一个应用程序使用以下代码截取 UIImageView 的屏幕截图 IBAction screenShot id sender UIGraphicsBeginImageContext sshot frame size self view
  • 等待池线程完成

    我很抱歉问了一个多余的问题 然而 我找到了许多解决我的问题的方法 但没有一个得到很好的解释 我希望在这里能说清楚 我的 C 应用程序的主线程使用线程池生成 1 n 个后台工作人员 我希望原始线程锁定 直到所有工作人员完成为止 我特别研究了
  • 两部分正态(或分裂正态)分布的密度

    两部分正态分布是否存在密度函数 在克兰上 我想在编写代码之前先检查一下 我检查了分发任务视图 它没有在那里列出 我查看了几个可能的软件包 但无济于事 更新 我已经添加了dsplitnorm psplitnorm qsplitnorm and
  • 打开 JavaScript 文件时 Visual Studio 2008 崩溃?

    每当我尝试打开特定的 JavaScript 文件时 Visual Studio Team system 2008 就会崩溃 但没有任何错误消息 我发现this http social msdn microsoft com Forums en
  • 在 django 中迁移数据的最佳方式是什么

    在我的模型中进行一些更改 例如模型中的新字段和新模型 后 将这些更改反映到我填充的数据库中的最佳方式是什么 PS 我想在一个地方看到许多解决方案的评级 显然已经列出了更多解决方案here https stackoverflow com qu
  • 是否使用任何未定义的不确定值或仅使用那些存储在具有自动存储的对象中的值?

    根据 C99 J 2 在以下情况下行为未定义 具有自动存储期限的对象的值在被使用时被使用 不定 那么对象具有不确定值的所有其他情况又如何呢 如果我们使用它们 我们是否也总是调用 UB 或者我们是否仅当它们包含陷阱表示时才调用 UB 示例包括
  • 当我单击单个标记时,React Google Map InfoWindow 显示所有信息

    由于某种原因 当我单击单个标记时 所有信息窗口都会显示 我希望当我单击一个标记时会出现一个信息窗口 有人可以解释为什么所有标记中的所有信息窗口都会显示吗 因此 当我关闭它时 InfoWindow 也会关闭 当我单击目标标记时 预期行为是 I
  • 开发 Android 应用程序时使用 Linux 操作系统重要吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我开始了 Android 开发 并意识到 Android 完全构建在 Linux 上 但我目前正在 Windows 上编程 我应该继续使用 Wi
  • 如何在 QTreeWidget 中找到所选项目?

    我有一个继承 QTreeWidget 的类 如何找到当前选定的行 通常我通过这种方式将信号连接到插槽 connect myButton SIGNAL triggered bool this SLOT myClick 但是 我找不到类似的东西
  • mySQL 查询多个 - 返回错误 mysql_fetch_array

    我有 2 个数据库表 用于预订系统 其结构如下 quartos id quarto tipo quarto vista quarto 储备 id reserva n cliente id quarto check in check out
  • 配置要在 Consumer 中传递的端点时添加元数据

    因此 当我配置端点并设置ep Consumer
  • 在 iPhone 和 Android 上完成 AES 加密

    我们正在 android 和 iphone 上创建相同的项目 现在的问题是我们需要使用Web服务以加密形式将用户信息发送到aspx服务器 我们都实现了它 但结果不同 因此是否有一种通用的 AES 加密方法可以产生相同的结果并可以在服务器上解
  • 如何从具有 null(0) 字符的 char 数组创建 C++ istringstream?

    我有一个 char 数组 其中随机位置包含空字符 我尝试使用此数组 encodedData arr 创建一个 iStringStream 如下所示 我使用此 iStringStream 将二进制数据 Iplimage 的 imagedata
  • 如何在内容页中使用 javascript,asp.net

    我从教程中获得了 jQuery 示例代码 它甚至是一个 Webform 示例 当我将它集成到我的项目并在内容页面中使用它时 JavaScript 不起作用 这是我的母版页代码
  • boost序列化多个对象

    稍微相关的问题this one https stackoverflow com questions 3870123 boost serialization multiple objects and this one https stacko
  • Google Spreadsheet API:清空工作表中的所有单元格

    我们使用 Google Sheet API v4 我们想要清除整个工作表的空数据 我们不想删除行 列 UpdateCells 调用不起作用 通过 API 删除列 工作 UpdateCells 调用 所有单元 我能够通过使用记录的清晰方法来做
  • 节点:一核,多进程

    我在网上查了一下 似乎找到的都是与 Node 如何从在多核 cpu 中运行中受益 这个问题相关的答案 但 如果您的计算机只有一个核心 则在任何给定时间只能运行一个进程 我正在考虑这里的任务调度 并且node使用单线程模型 我的问题 是否有任
  • 从 Air 在“新窗口”中打开另一个 Android 应用程序

    这是一个little涉及 所以请耐心等待 我有两个 Android 应用程序 一个是 Adob e Air 内置的 启动器 另一个是从 google play 商店下载的 目标 在某个时刻 我希望 启动器 打开 目 标 并切换到它 目前我正
  • Asp Core:Azure 广告身份验证 + 自定义 JWT + 自定义身份存储

    使用 ASP NET Core 2 0 我尝试实现以下目标 通过 Azure AD 进行身份验证 注册应用程序 Custom JWT as the authentication scheme to 使 Web 应用程序身份验证能够跨服务器