gpt4 book ai didi

asp.net-core - 如何在 asp.net core 2.x Web 应用程序中全局实现基于 AD 组的授权?

转载 作者:行者123 更新时间:2023-12-03 18:06:35 32 4
gpt4 key购买 nike

我想知道是否有人可以为我指出一个方向或一个示例,其中包含完整的代码让我得到一个整体的想法?

谢谢。

更新:
我在 Startup.cs 中只有以下代码,并确保在 launchSettings.json 中 windowsAutication 为 true。

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();

services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
//.RequireRole(@"Departmental - Information Technology - Development") // Works
.RequireRole(@"*IT.Center of Excellence.Digital Workplace") // Error
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});

}

我想我已经启用了身份验证并尝试授权指定 AD 组中的用户在全局级别访问应用程序。

如果我使用注释的 RequireRole 它可以工作,但使用未注释的 RequireRole 它会给我这个错误:
Win32Exception: 主域和受信任域之间的信任关系失败。

堆栈的顶行显示:
System.Security.Principal.NTAccount.TranslateToSids(IdentityReferenceCollection sourceAccounts,out bool someFailed)

知道为什么吗?

我对以上更新的理解

RequireRole 中指定的组名似乎是电子邮件分发列表而不是安全组。如果我使用其他一些 AD 组,它可以工作,但会出现这个新错误:

InvalidOperationException:没有指定 authenticationScheme,也没有找到 DefaultForbidScheme。

如果我在 Startup.cs 的 ConfigureServices 中添加 IIS 默认 authenticationScheme
services.AddAuthentication(IISDefaults.AuthenticationScheme);

它给了我一个 HTTP 403 页面:该网站拒绝显示此网页

所以这是最终的代码:

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();

services.AddAuthentication(IISDefaults.AuthenticationScheme);

services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole(@"Departmental - Information Technology - Development") // AD security group
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});

}

如果我理解错误,请纠正我。谢谢你。

最佳答案

选项 1:Windows 身份验证

您可以为 Intranet 应用程序打开 Windows 身份验证。阅读文档 here .您可以通过执行 this 之类的操作来检查用户是否属于某个角色/组。 .

在此之前,您可以通过执行 gpresult /R 检查您的计算机加入的组信息。在命令提示符中。见 this post了解更多信息。

User.IsInRole("xxxx")  // this should return True for any group listed up there

如果您不需要获取任何与 Windows 相关的信息,则无需将当前主体转换为 Windows 主体。

如果你想得到所有组的列表,你仍然需要查询你的 AD。

警告:

有时我使用 gpresult /R 看到一些组没有出现在结果中。在计算机上,与选项 2 方法相比。这就是为什么有时当你这样做 User.IsInRole()它返回false。我仍然不知道为什么会这样。

选项 2:使用 AD 查找的表单例份验证

Windows 身份验证仅提供有关用户和 AD 组的少量信息。有时这就足够了,但大多数时候还不够。

您还可以使用常规表单例份验证并与下面的 AD 对话并发出 cookie。这样,虽然用户需要使用他们的 Windows 凭据和密码登录到您的应用程序,但您可以完全控制 AD 信息。

您不想手动编写所有内容。幸好有图书馆 Novell.Directory.Ldap.NET 标准 帮助。你可以在 NuGet 中找到它。

用于定义 AD 所需内容的接口(interface)以及登录协议(protocol):

namespace DL.SO.Services.Core
{
public interface IAppUser
{
string Username { get; }
string DisplayName { get; }
string Email { get; }
string[] Roles { get; }
}

public interface IAuthenticationService
{
IAppUser Login(string username, string password);
}
}

应用用户实现:

using DL.SO.Services.Core;

namespace DL.SO.Services.Security.Ldap.Entities
{
public class AppUser : IAppUser
{
public string Username { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string[] Roles { get; set; }
}
}

用于从 appsettings.json 映射值的 Ldap 配置对象:

namespace DL.SO.Services.Security.Ldap
{
public class LdapConfig
{
public string Url { get; set; }
public string BindDn { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string SearchBase { get; set; }
public string SearchFilter { get; set; }
}
}

LdapAuthenticationService 实现:

using Microsoft.Extensions.Options;
using Novell.Directory.Ldap;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using DL.SO.Services.Core;
using DL.SO.Services.Security.Ldap.Entities;

namespace DL.SO.Services.Security.Ldap
{
public class LdapAuthenticationService : IAuthenticationService
{
private const string MemberOfAttribute = "memberOf";
private const string DisplayNameAttribute = "displayName";
private const string SAMAccountNameAttribute = "sAMAccountName";
private const string MailAttribute = "mail";

private readonly LdapConfig _config;
private readonly LdapConnection _connection;

public LdapAuthenticationService(IOptions<LdapConfig> configAccessor)
{
_config = configAccessor.Value;
_connection = new LdapConnection();
}

public IAppUser Login(string username, string password)
{
_connection.Connect(_config.Url, LdapConnection.DEFAULT_PORT);
_connection.Bind(_config.Username, _config.Password);

var searchFilter = String.Format(_config.SearchFilter, username);
var result = _connection.Search(
_config.SearchBase,
LdapConnection.SCOPE_SUB,
searchFilter,
new[] {
MemberOfAttribute,
DisplayNameAttribute,
SAMAccountNameAttribute,
MailAttribute
},
false
);

try
{
var user = result.next();
if (user != null)
{
_connection.Bind(user.DN, password);
if (_connection.Bound)
{
var accountNameAttr = user.getAttribute(SAMAccountNameAttribute);
if (accountNameAttr == null)
{
throw new Exception("Your account is missing the account name.");
}

var displayNameAttr = user.getAttribute(DisplayNameAttribute);
if (displayNameAttr == null)
{
throw new Exception("Your account is missing the display name.");
}

var emailAttr = user.getAttribute(MailAttribute);
if (emailAttr == null)
{
throw new Exception("Your account is missing an email.");
}

var memberAttr = user.getAttribute(MemberOfAttribute);
if (memberAttr == null)
{
throw new Exception("Your account is missing roles.");
}

return new AppUser
{
DisplayName = displayNameAttr.StringValue,
Username = accountNameAttr.StringValue,
Email = emailAttr.StringValue,
Roles = memberAttr.StringValueArray
.Select(x => GetGroup(x))
.Where(x => x != null)
.Distinct()
.ToArray()
};
}
}
}
finally
{
_connection.Disconnect();
}

return null;
}

private string GetGroup(string value)
{
Match match = Regex.Match(value, "^CN=([^,]*)");
if (!match.Success)
{
return null;
}

return match.Groups[1].Value;
}
}
}

appsettings.json 中的配置(只是一个例子):

{
"ldap": {
"url": "[YOUR_COMPANY].loc",
"bindDn": "CN=Users,DC=[YOUR_COMPANY],DC=loc",
"username": "[YOUR_COMPANY_ADMIN]",
"password": "xxx",
"searchBase": "DC=[YOUR_COMPANY],DC=loc",
"searchFilter": "(&(objectClass=user)(objectClass=person)(sAMAccountName={0}))"
},
"cookies": {
"cookieName": "cookie-name-you-want-for-your-app",
"loginPath": "/account/login",
"logoutPath": "/account/logout",
"accessDeniedPath": "/account/accessDenied",
"returnUrlParameter": "returnUrl"
}
}

为应用程序设置身份验证(也可能是授权):

namespace DL.SO.Web.UI
{
public class Startup
{
private readonly IHostingEnvironment _currentEnvironment;
public IConfiguration Configuration { get; private set; }

public Startup(IConfiguration configuration, IHostingEnvironment env)
{
_currentEnvironment = env;
Configuration = configuration;
}

public void ConfigureServices(IServiceCollection services)
{
// Authentication service
services.Configure<LdapConfig>(this.Configuration.GetSection("ldap"));
services.AddScoped<IAuthenticationService, LdapAuthenticationService>();

// MVC
services.AddMvc(config =>
{
// Requiring authenticated users on the site globally
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()

// You can chain more requirements here
// .RequireRole(...) OR
// .RequireClaim(...) OR
// .Requirements.Add(...)

.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

// Authentication
var cookiesConfig = this.Configuration.GetSection("cookies")
.Get<CookiesConfig>();
services.AddAuthentication(
CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = cookiesConfig.CookieName;
options.LoginPath = cookiesConfig.LoginPath;
options.LogoutPath = cookiesConfig.LogoutPath;
options.AccessDeniedPath = cookiesConfig.AccessDeniedPath;
options.ReturnUrlParameter = cookiesConfig.ReturnUrlParameter;
});

// Setup more authorization policies as an example.
// You can use them to protected more strict areas. Otherwise
// you don't need them.
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly",
policy => policy.RequireClaim(ClaimTypes.Role, "[ADMIN_ROLE_OF_YOUR_COMPANY]"));

// More on Microsoft documentation
// https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1
});
}

public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc(...);
}
}
}

如何使用身份验证服务对用户进行身份验证:

namespace DL.SO.Web.UI.Controllers
{
public class AccountController : Controller
{
private readonly IAuthenticationService _authService;

public AccountController(IAuthenticationService authService)
{
_authService = authService;
}

[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
try
{
var user = _authService.Login(model.Username, model.Password);

// If the user is authenticated, store its claims to cookie
if (user != null)
{
var userClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Username),
new Claim(CustomClaimTypes.DisplayName, user.DisplayName),
new Claim(ClaimTypes.Email, user.Email)
};

// Roles
foreach (var role in user.Roles)
{
userClaims.Add(new Claim(ClaimTypes.Role, role));
}

var principal = new ClaimsPrincipal(
new ClaimsIdentity(userClaims, _authService.GetType().Name)
);

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal,
new AuthenticationProperties
{
IsPersistent = model.RememberMe
}
);

return Redirect(Url.IsLocalUrl(model.ReturnUrl)
? model.ReturnUrl
: "/");
}

ModelState.AddModelError("", @"Your username or password
is incorrect. Please try again.");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
}
return View(model);
}
}
}

如何读取存储在声明中的信息:

public class TopNavbarViewComponent : ViewComponent
{
private readonly IHttpContextAccessor _httpContextAccessor;

public TopNavbarViewComponent(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public async Task<IViewComponentResult> InvokeAsync()
{
string loggedInUsername = _httpContextAccessor.HttpContext.User.Identity.Name;

string loggedInUserDisplayName = _httpContextAccessor.HttpContext.User.GetDisplayName();

...
return View(vm);
}
}

ClaimsPrincipal 的扩展方法:

namespace DL.SO.Framework.Mvc.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static Claim GetClaim(this ClaimsPrincipal user, string claimType)
{
return user.Claims
.SingleOrDefault(c => c.Type == claimType);
}

public static string GetDisplayName(this ClaimsPrincipal user)
{
var claim = GetClaim(user, CustomClaimTypes.DisplayName);

return claim?.Value;
}

public static string GetEmail(this ClaimsPrincipal user)
{
var claim = GetClaim(user, ClaimTypes.Email);

return claim?.Value;
}
}
}

如何使用策略授权:

namespace DL.SO.Web.UI.Areas.Admin.Controllers
{
[Area("admin")]
[Authorize(Policy = "AdminOnly")]
public abstract class AdminControllerBase : Controller {}
}

奖金

您可以下载 AD Explorer from Microsoft这样您就可以可视化您的公司广告。

哎呀。我本来打算先给出一些东西,但我最终写了一篇很长的文章。

关于asp.net-core - 如何在 asp.net core 2.x Web 应用程序中全局实现基于 AD 组的授权?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50121216/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com