使用 .Net Core 中的 Windows 已验证应用程序填充来自 SQL 的自定义声明
Populate custom claim from SQL with Windows Authenticated app in .Net Core
场景 - Active Directory 中的 .Net Core Intranet 应用程序使用 SQL 服务器来管理应用程序特定权限和扩展用户身份。
迄今为止的成功 - 用户已通过身份验证并且 windows 声明可用(名称和组)。 Identity.Name 可用于 return 来自具有扩展属性的数据库的域用户模型。
问题和问题 - 然后我试图填充一个自定义声明 属性 "Id" 并通过 ClaimsPrincipal 在全球范围内提供。迄今为止,我已经研究过 ClaimsTransformation,但没有取得多大成功。在我读到的其他文章中,您必须在登录之前添加声明,但这真的是真的吗?这将意味着完全依赖 AD 来履行所有索赔,真的是这样吗?
下面是我此时在 HomeController 中的简单代码。我正在访问数据库,然后尝试填充 ClaimsPrincipal,然后 return 域用户模型。我认为这可能是我的问题所在,但我是 .net 中授权的新手,并且正在努力解决索赔问题。
非常感谢收到的所有帮助
当前代码:
public IActionResult Index()
{
var user = GetExtendedUserDetails();
User.Claims.ToList();
return View(user);
}
private Models.User GetExtendedUserDetails()
{
var user = _context.User.SingleOrDefault(m => m.Username == User.Identity.Name.Remove(0, 6));
var claims = new List<Claim>();
claims.Add(new Claim("Id", Convert.ToString(user.Id), ClaimValueTypes.String));
var userIdentity = new ClaimsIdentity("Intranet");
userIdentity.AddClaims(claims);
var userPrincipal = new ClaimsPrincipal(userIdentity);
return user;
}
更新:
我已经注册了 ClaimsTransformation
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
并根据此 github 查询构建了如下所示的 ClaimsTransformer
https://github.com/aspnet/Security/issues/863
public class ClaimsTransformer : IClaimsTransformer
{
private readonly TimesheetContext _context;
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
System.Security.Principal.WindowsIdentity windowsIdentity = null;
foreach (var i in context.Principal.Identities)
{
//windows token
if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
{
windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
}
}
if (windowsIdentity != null)
{
//find user in database
var username = windowsIdentity.Name.Remove(0, 6);
var appUser = _context.User.FirstOrDefaultAsync(m => m.Username == username);
if (appUser != null)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id)));
/*//add all claims from security profile
foreach (var p in appUser.Id)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
}*/
}
}
return await System.Threading.Tasks.Task.FromResult(context.Principal);
}
}
但我收到 NullReferenceException:对象引用未设置到对象实例错误,尽管之前已经 return 编辑了域模型。
与 STARTUP.CS
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Birch.Intranet.Models;
using Microsoft.EntityFrameworkCore;
namespace Birch.Intranet
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization();
// Add framework services.
services.AddMvc();
// Add database
var connection = @"Data Source=****;Initial Catalog=Timesheet;Integrated Security=True;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
services.AddDbContext<TimesheetContext>(options => options.UseSqlServer(connection));
// Add session
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.CookieName = ".Intranet";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
app.UseSession();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
您需要使用 IClaimsTransformer
依赖注入。
app.UseClaimsTransformation(async (context) =>
{
IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
return await transformer.TransformAsync(context);
});
// Register
services.AddScoped<IClaimsTransformer, ClaimsTransformer>();
并且需要在ClaimsTransformer
中注入DbContext
public class ClaimsTransformer : IClaimsTransformer
{
private readonly TimesheetContext _context;
public ClaimsTransformer(TimesheetContext dbContext)
{
_context = dbContext;
}
// ....
}
场景 - Active Directory 中的 .Net Core Intranet 应用程序使用 SQL 服务器来管理应用程序特定权限和扩展用户身份。
迄今为止的成功 - 用户已通过身份验证并且 windows 声明可用(名称和组)。 Identity.Name 可用于 return 来自具有扩展属性的数据库的域用户模型。
问题和问题 - 然后我试图填充一个自定义声明 属性 "Id" 并通过 ClaimsPrincipal 在全球范围内提供。迄今为止,我已经研究过 ClaimsTransformation,但没有取得多大成功。在我读到的其他文章中,您必须在登录之前添加声明,但这真的是真的吗?这将意味着完全依赖 AD 来履行所有索赔,真的是这样吗?
下面是我此时在 HomeController 中的简单代码。我正在访问数据库,然后尝试填充 ClaimsPrincipal,然后 return 域用户模型。我认为这可能是我的问题所在,但我是 .net 中授权的新手,并且正在努力解决索赔问题。
非常感谢收到的所有帮助
当前代码:
public IActionResult Index()
{
var user = GetExtendedUserDetails();
User.Claims.ToList();
return View(user);
}
private Models.User GetExtendedUserDetails()
{
var user = _context.User.SingleOrDefault(m => m.Username == User.Identity.Name.Remove(0, 6));
var claims = new List<Claim>();
claims.Add(new Claim("Id", Convert.ToString(user.Id), ClaimValueTypes.String));
var userIdentity = new ClaimsIdentity("Intranet");
userIdentity.AddClaims(claims);
var userPrincipal = new ClaimsPrincipal(userIdentity);
return user;
}
更新:
我已经注册了 ClaimsTransformation
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
并根据此 github 查询构建了如下所示的 ClaimsTransformer
https://github.com/aspnet/Security/issues/863
public class ClaimsTransformer : IClaimsTransformer
{
private readonly TimesheetContext _context;
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
System.Security.Principal.WindowsIdentity windowsIdentity = null;
foreach (var i in context.Principal.Identities)
{
//windows token
if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
{
windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
}
}
if (windowsIdentity != null)
{
//find user in database
var username = windowsIdentity.Name.Remove(0, 6);
var appUser = _context.User.FirstOrDefaultAsync(m => m.Username == username);
if (appUser != null)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id)));
/*//add all claims from security profile
foreach (var p in appUser.Id)
{
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
}*/
}
}
return await System.Threading.Tasks.Task.FromResult(context.Principal);
}
}
但我收到 NullReferenceException:对象引用未设置到对象实例错误,尽管之前已经 return 编辑了域模型。
与 STARTUP.CS
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Birch.Intranet.Models;
using Microsoft.EntityFrameworkCore;
namespace Birch.Intranet
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization();
// Add framework services.
services.AddMvc();
// Add database
var connection = @"Data Source=****;Initial Catalog=Timesheet;Integrated Security=True;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
services.AddDbContext<TimesheetContext>(options => options.UseSqlServer(connection));
// Add session
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.CookieName = ".Intranet";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));
app.UseSession();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
您需要使用 IClaimsTransformer
依赖注入。
app.UseClaimsTransformation(async (context) =>
{
IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
return await transformer.TransformAsync(context);
});
// Register
services.AddScoped<IClaimsTransformer, ClaimsTransformer>();
并且需要在ClaimsTransformer
中注入DbContext
public class ClaimsTransformer : IClaimsTransformer
{
private readonly TimesheetContext _context;
public ClaimsTransformer(TimesheetContext dbContext)
{
_context = dbContext;
}
// ....
}