用于身份验证的Azure Active Directory和用于授权的ASP.NET核心标识

时间:2017-12-04 22:32:41

标签: asp.net-core asp.net-core-2.0

我想创建一个ASP.NET Core 2.0应用程序,它使用Azure Active Directory作为身份提供程序(用于身份验证),但ASP.NET Core Identity用于授权(例如使用控制器属性,如'[Authorize(Roles =“Admin) “)]')。在解决方案中,我期望本地身份数据库表AspNetUserLogins to hold references to the Azure Active Directory identities

我认为该解决方案将涉及claim transformation使用从ASP.NET Core Identity中提取的角色来装饰经过身份验证的用户。

我的问题:

  1. 我可以从Visual Studio解决方案模板中获取Azure Active Directory身份验证,但是我无法弄清楚如何在Startup.ConfigureServices中的某处添加和配置ASP.NET核心身份(例如services.AddIdentity()等) ())
  2. 我想知道正确的钩子在哪里进行声明转换。 (例如OpenIdConnectEvents.OnTokenValidated或者可能是AccountController方法)
  3. 重现我的基线的步骤......

    1. 在portal.azure.com中......

      • Azure Active Directory>应用注册>新申请注册(您可以稍后删除)
      • 为应用程序命名并将其设置为“Wep app / API”
      • 将“登录网址”设置为something arbitrary like 'https://blabla'
      • 转到新创建的应用注册并复制“应用ID”
    2. 使用Visual Studio 2017 15.4.2 ...

      创建ASP.NET Core 2.0项目
      • ASP.NET核心Web应用程序
      • .NET Framework,ASP.NET Core 2.0,'Web应用程序'
      • 更改身份验证> “工作或学校帐户”
      • 选择'云 - 单一组织'
      • 输入您的域名 .onmicrosoft.com'(我猜)
      • 单击向导以创建项目
      • 编辑appsettings.json并将'ClientId'更改为'Application ID'(从portal.azure.com复制)
      • 复制为'CallbackPath'设置的值(例如'/ signin-oidc')
      • 转到项目属性>调试>并复制IIS Express https url(例如'https:// localhost:44366 /')
      • 切换回portal.azure.com中的新应用注册
      • '回复网址'>从上面两条信息的串联中添加一个新的回复URL(例如'https:// localhost:44366 / signin-oidc')
      • 点击“保存”
      • 在Visual Studio中运行项目
      • 使用Azure Active Directory帐户登录(系统会要求您同意应用程序所需的权限)
      • 您应该最终进入ASP.NET核心演示页
    3. 我不太确定...

      (我借用模板生成的解决方案中的代码,将“身份验证选项”设置为“个人用户帐户”>'在应用内存储用户帐户'。)

      • 添加nuget包Microsoft.AspNetCore.Identity.EntityFrameworkCore(我必须首先将Microsoft.AspNetCore.Authentication.Cookies从2.0.0升级到2.0.1,但要安装)
      • 添加nuget包Microsoft.EntityFrameworkCore.SqlServer
      • 添加以下类

        public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
        {
            public ApplicationDbContext(DbContextOptions<AspNetCoreIdentity.Data.ApplicationDbContext> options) : base(options)
            {
            }
        
            protected override void OnModelCreating(ModelBuilder builder)
            {
                base.OnModelCreating(builder);
            }
        }
        
        public class ApplicationUser : IdentityUser
        {
        }
        
      • 在Startup.ConfigureServices()

        的开头添加以下内容
        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
        
      • 将连接字符串添加到appsettings.json(假设localhost上的默认SQL Server实例和名为'AspNetCoreIdentity'的身份数据库)

        "ConnectionStrings": {
          "DefaultConnection": "Data Source=.\\;Initial Catalog=AspNetCoreIdentity;Integrated Security=True;MultipleActiveResultSets=True"
        }
        

      现在,当我再次运行应用程序时,我最终进入了一个重定向循环,我认为在我的应用程序和Azure Active Directory之间运行。追踪节目......

      Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed for user: (null). Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker: Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.

      然后我尝试将方法添加到AccountController(Login,ExternalLogin),希望我能打到一个断点,但现在我真的卡住了。

      其他参考资料......

1 个答案:

答案 0 :(得分:2)

我认为我有这个工作,但我对这个框架很陌生,所以欢迎批评这种方法。

在启动时,我不得不向微软的例子添加两件事。

  1. DefaultSignInScheme 值设置为 AuthenticationProperties ,以防止授权失败时发生Stackoverflow异常(请参阅here)。
  2. 将访问被拒绝路径添加到应用程序cookie
  3. Startup.cs:

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

    然后我扩展了 AzureAdAuthenticationBuilderExtensions &gt; ConfigureAzureOptions 类,并且在发生令牌验证事件时正在做一些额外的工作(即从角色存储中加载用户角色)

    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;
    
                options.Events = new OpenIdConnectEvents
                {                    
                    OnTokenValidated = (context) =>
                    {                           
                        // Load roles from role store here
                        var roles = new List<string>() { "Admin" };
                        var claims = new List<Claim>();
                        foreach (var role in roles) claims.Add(new Claim(ClaimTypes.Role, role));
    
                        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                        context.Principal.AddIdentity(claimsIdentity);
    
                        return Task.CompletedTask;       
                    }                    
                };
            }