【问题标题】:Is ASP.NET Core Identity needed for Intranet app using Windows Authentication使用 Windows 身份验证的 Intranet 应用程序是否需要 ASP.NET Core 标识
【发布时间】:2018-04-10 09:10:43
【问题描述】:

在 Intranet Web 应用程序中使用 Windows 身份验证我想实现以下目标:

  • 从 AD 中收集其他属性(姓名、员工编号)
  • 从数据库表中收集其他属性(工作时间、工资)
  • 根据应用程序角色(而非 AD 组)进行授权
  • 根据 AD 属性进行授权(具有直接下属)
  • 用户未提供用户名/密码

在我寻找答案时,建议我需要将ClaimsTransformation 添加到我的应用程序中:

How do I use Windows Authentication with users in database

Populate custom claim from SQL with Windows Authenticated app in .Net Core

Caching Claims in .net core 2.0

虽然我不完全理解解决方案以及为什么ClaimsTransformation 在每个请求中都会出现,所以我正在寻找以下问题的答案:

  1. ClaimsTransformation 工作是否需要 ASP.NET Core 标识?
  2. ClaimsTransformation 是否在仅使用 Windows 身份验证或还使用基于表单的身份验证时发生在每个请求上?
  3. 每个请求都必须这样做吗?
  4. 缓存诸如 GivenName、Surname 之类的声明似乎很简单,但角色呢?需要采取哪些步骤来确保不会每次都命中数据库,但角色会在发生更改时得到更新。
  5. 对于我正在尝试做的事情,是否有更简单的替代方法?

【问题讨论】:

  • 1.不,2. 每个请求。 5. 似乎您可以使用 windows auth 来初始登录 Identity,进行数据库查找,并发出 Identity cookie 以用于后续请求。这个世界比对每个请求进行声明转换更有效率。
  • 您能否为我指明正确的方向,了解如何实现您的建议?我假设用户必须登录才能创建 cookie 但不提供任何凭据?

标签: asp.net-core asp.net-identity windows-authentication claims


【解决方案1】:

这是一个使用 IClaimsTransformation 的替代方案(使用 .NET 6)

几点说明:

在 ClaimsTransformer 类中,必须克隆现有的 ClaimsPrincipal 并将您的 Claims 添加到 那个,而不是尝试修改现有的 ClaimsPrincipal。然后必须在 ConfigureServices() 中将其注册为单例。

在 mheptinstall 的回答中使用的设置 AccessDeniedPath 的技术在这里不起作用,相反我必须使用 UseStatusCodePages() 方法来重定向到自定义页面以解决 403 错误。

必须使用newIdentity.RoleClaimType 类型创建新声明,而不是System.Security.Claims.ClaimTypes.Role,否则 AuthorizeAttribute(例如[Authorize(Roles = "Admin")])将不起作用

显然应用程序将设置为使用 Windows 身份验证。

ClaimsTransformer.cs

public class ClaimsTransformer : IClaimsTransformation
{
    // Can consume services from DI as needed, including scoped DbContexts
    public ClaimsTransformer(IHttpContextAccessor httpAccessor) { }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // Clone current identity
        var clone = principal.Clone();
        var newIdentity = (ClaimsIdentity)clone.Identity;

        // Get the username
        var username = principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier || c.Type == ClaimTypes.Name).Value;

        if (username == null)
        {
            return principal;
        }

        // Get the user roles from the database using the username we've just obtained
        // Ideally these would be cached where possible
        
        // ...

        // Add role claims to cloned identity
        foreach (var roleName in roleNamesFromDatabase)
        {
            var claim = new Claim(newIdentity.RoleClaimType, roleName);
            newIdentity.AddClaim(claim);
        }

        return clone;
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(IISDefaults.AuthenticationScheme);
    services.AddAuthorization();
    services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();

    services.AddMvc().AddRazorRuntimeCompilation();

    // ...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePages(async context => {
        if (context.HttpContext.Response.StatusCode == 403)
        {
            context.HttpContext.Response.Redirect("/Home/AccessDenied");
        }
    });

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

示例 HomeController.cs

[Authorize]
public class HomeController : Controller
{
    public HomeController()
    { }

    public IActionResult Index()
    {
        return View();
    }

    [Authorize(Roles = "Admin")]
    public IActionResult AdminOnly()
    {
        return View();
    }

    [AllowAnonymous]
    public IActionResult AccessDenied()
    {
        return View();
    }
}

【讨论】:

    【解决方案2】:

    article 给了我一些想法,这是一个可能的解决方案。

    控制器将从基础控制器继承,该基础控制器具有需要 Authenticated 声明的策略。当这不存在时,它会转到AccessDeniedPath 并静默执行登录,添加Authenticated 声明以及任何其他声明,如果这已经存在,则会出现拒绝访问消息。

    在创建新的 ClaimsIdentity 时,我不得不删除原始身份中的大部分声明,因为我收到了 HTTP 400 - Bad Request (Request Header too long) 错误消息。

    这种方法有什么明显的问题吗?

    Startup.cs

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
    
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(options =>
                {
                    options.LoginPath = "/Home/Login";
                    options.AccessDeniedPath = "/Home/AccessDenied";
                });
    
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Authenticated",
                    policy => policy.RequireClaim("Authenticated"));
                options.AddPolicy("Admin",
                    policy => policy.RequireClaim("Admin"));
            });
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
    
            app.UseStaticFiles();
            app.UseAuthentication();
    
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    

    控制器

    [Authorize(Policy = "Authenticated")]
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    
        [Authorize(Policy = "Admin")]
        public IActionResult About()
        {
            return View();
        }
    
        [AllowAnonymous]
        public async Task<IActionResult> Login(string returnUrl)
        {
            var identity = ((ClaimsIdentity)HttpContext.User.Identity);
    
            var claims = new List<Claim>
            {
                new Claim("Authenticated", "True"),
                new Claim(ClaimTypes.Name,
                    identity.FindFirst(c => c.Type == ClaimTypes.Name).Value),
                new Claim(ClaimTypes.PrimarySid,
                    identity.FindFirst(c => c.Type == ClaimTypes.PrimarySid).Value)
            };
    
            var claimsIdentity = new ClaimsIdentity(
                claims,
                identity.AuthenticationType,
                identity.NameClaimType,
                identity.RoleClaimType);
    
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                new AuthenticationProperties());
    
            return Redirect(returnUrl);
        }
    
        [AllowAnonymous]
        public IActionResult AccessDenied(string returnUrl)
        {
            if (User.FindFirst("Authenticated") == null)
                return RedirectToAction("Login", new { returnUrl });
    
            return View();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-04-17
      • 2014-02-05
      • 2021-09-08
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      • 2011-06-27
      • 1970-01-01
      相关资源
      最近更新 更多