我想知道是否有人可以指出我的方向或示例,其中包含完整的代码,以便我获得整体想法?
感谢。
更新 我只在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));
});
}
如果我理解错误,请纠正我。谢谢。
答案 0 :(得分:3)
您可以为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的原因。我仍然不知道为什么会这样。
Windows身份验证仅提供有关用户和AD组的一些信息。有时候这已经够了,但大部分时间都没有。
您还可以使用常规的表单身份验证并与下面的AD通信并发出cookie。这样,虽然用户需要使用他们的Windows凭据和密码登录您的应用程序,但您可以完全控制AD信息。
你不想手工编写所有东西。幸运的是,有一个库 Novell.Directory.Ldap.NETStandard 可以提供帮助。你可以在NuGet找到它。
用于定义AD所需内容的接口,以及登录协议:
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);
}
}
AppUser实施:
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,以便可视化您的公司AD。
哎呀。我打算只是给出一些先发制人的东西,但我最后写了一篇很长的帖子。