Azure Active Directory 用于认证,ASP.NET Core Identity 用于授权。

14
我想创建一个ASP.NET Core 2.0应用程序,使用Azure Active Directory作为身份提供者(用于身份验证),但是使用ASP.NET Core Identity进行授权(例如使用控制器属性如'[Authorize(Roles = "Admin")]')。在解决方案中,我希望本地Identity数据库表AspNetUserLogins保留对Azure Active Directory标识的引用
我认为解决方案涉及声明转换,以从ASP.NET Core Identity获取角色并对已验证的用户进行装饰。
我的问题:
1.我可以从Visual Studio解决方案模板中使Azure Active Directory身份验证工作,但是我无法弄清楚如何添加和配置ASP.NET Core Identity(例如services.AddIdentity()等,在Startup.ConfigureServices()的某个位置)。
2.我想知道正确的钩子在哪里执行声明转换(例如OpenIdConnectEvents.OnTokenValidated或可能是AccountController方法)。
重现基线的步骤...
  1. 在 portal.azure.com 中...

    • Azure Active Directory > 应用注册 > 新建应用注册 (稍后可以删除)
    • 给应用命名并设置为 'Web应用程序/API'
    • 将'Sign-on URL'设置为 类似于 'https://blabla' 的任意内容
    • 进入新创建的应用注册并复制'Application ID'
  2. 使用 Visual Studio 2017 15.4.2 创建 ASP.NET Core 2.0 项目...

    • ASP.NET Core Web 应用程序
    • .NET Framework、ASP.NET Core 2.0、'Web应用程序'
    • 更改身份验证 > '工作或学校帐户'
    • 选择'云-单个组织'
    • 输入你的域 'something.onmicrosoft.com' (我猜测)
    • 按照向导的提示创建项目
    • 编辑 appsettings.json 并将 'ClientId' 更改为从 portal.azure.com 复制的 'Application ID'
    • 复制设置为 'CallbackPath' 的值 (例如 '/signin-oidc')
    • 转到项目属性 > 调试 > 并复制 IIS Express https url (例如 'https://localhost:44366/')
    • 切换回 portal.azure.com 中的新应用注册
    • 'Reply URLs' > 添加一个由上述两个信息串联而成的新回复 URL (例如 'https://localhost:44366/signin-oidc')
    • 点击 '保存'
    • 在 Visual Studio 中运行项目
    • 使用你的 Azure Active Directory 帐户登录 (你将被要求同意应用程序所需的权限)
    • 然后你应该会进入 ASP.NET Core 演示页面

从这里开始我不太确定……

(我从模板生成的解决方案中借用了代码,'身份验证选项'设置为'个人用户账户' > '在应用程序中存储用户账户。')

  • Add nuget package Microsoft.AspNetCore.Identity.EntityFrameworkCore (I had to upgrade Microsoft.AspNetCore.Authentication.Cookies from 2.0.0 to 2.0.1 first though for it to install)
  • Add nuget package Microsoft.EntityFrameworkCore.SqlServer
  • Add the following classes

    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
    {
    }
    
  • Add the following at the beginning of Startup.ConfigureServices()

    services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
  • Add connection string to appsettings.json (assumes default SQL Server instance on localhost and identity database named 'AspNetCoreIdentity')

    "ConnectionStrings": {
      "DefaultConnection": "Data Source=.\\;Initial Catalog=AspNetCoreIdentity;Integrated Security=True;MultipleActiveResultSets=True"
    }
    
现在当我再次运行应用程序时,我发现自己陷入了一个重定向循环中,我认为这个循环在我的应用程序和Azure Active Directory登录之间运行。跟踪显示...
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: 信息: 用户的授权失败:(null)。 Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker: 信息: 在过滤器“Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter”处请求的授权失败。
然后我尝试在AccountController(Login,ExternalLogin)中添加方法,希望我能够触发断点,但现在我真的卡住了。
其他参考...

1
你已经有解决方案了吗? - Tom Kluskens
1
今天显然是我们都来这里寻找答案的日子。有什么运气吗?哈哈。 - John Hargrove
我仍然没有解决这个问题。 - jimalad
最好从身份模板开始,然后模仿添加Facebook认证的指示,但是替换为OpenIdConnect会更容易。 - Tratcher
你找到解决方案了吗? - Hills
1个回答

2

我觉得我已经做到了,但是因为我对这个框架还很新,所以对这种方法的批评是受欢迎的。

在启动时,我需要向 Microsoft 的示例添加两个东西。

  1. DefaultSignInScheme 值设置为 AuthenticationProperties,以防止授权失败时出现 Stackoverflow 异常(请参见此处)。
  2. 将访问被拒绝的路径添加到应用程序 cookie 中

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接