После нескольких недель исследований Identity 2.0, олицетворения, делегирования и Kerberos я все еще не могу найти решение, которое позволило бы мне выдать себя за пользователя ClaimsIdentity, которого я создал с помощью OWIN в своем приложении MVC. Особенности моего сценария следующие.
Аутентификация Windows отключена + Аноним включен.
Я использую класс запуска OWIN для ручной аутентификации пользователя в нашей Active Directory. Затем я упаковываю некоторые свойства в файл cookie, который доступен в остальной части приложения. Это - ссылка, на которую я ссылался при настройке этих классов.
Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = MyAuthentication.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider(),
CookieName = "SessionName",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromHours(double.Parse(ConfigurationManager.AppSettings["CookieLength"]))
});
AuthenticationService.cs
using System;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices;
using System.Security.Claims;
using Microsoft.Owin.Security;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
namespace mine.Security
{
public class AuthenticationService
{
private readonly IAuthenticationManager _authenticationManager;
private PrincipalContext _context;
private UserPrincipal _userPrincipal;
private ClaimsIdentity _identity;
public AuthenticationService(IAuthenticationManager authenticationManager)
{
_authenticationManager = authenticationManager;
}
/// <summary>
/// Check if username and password matches existing account in AD.
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
public AuthenticationResult SignIn(String username, String password)
{
// connect to active directory
_context = new PrincipalContext(ContextType.Domain,
ConfigurationManager.ConnectionStrings["LdapServer"].ConnectionString,
ConfigurationManager.ConnectionStrings["LdapContainer"].ConnectionString,
ContextOptions.SimpleBind,
ConfigurationManager.ConnectionStrings["LDAPUser"].ConnectionString,
ConfigurationManager.ConnectionStrings["LDAPPass"].ConnectionString);
// try to find if the user exists
_userPrincipal = UserPrincipal.FindByIdentity(_context, IdentityType.SamAccountName, username);
if (_userPrincipal == null)
{
return new AuthenticationResult("There was an issue authenticating you.");
}
// try to validate credentials
if (!_context.ValidateCredentials(username, password))
{
return new AuthenticationResult("Incorrect username/password combination.");
}
// ensure account is not locked out
if (_userPrincipal.IsAccountLockedOut())
{
return new AuthenticationResult("There was an issue authenticating you.");
}
// ensure account is enabled
if (_userPrincipal.Enabled.HasValue && _userPrincipal.Enabled.Value == false)
{
return new AuthenticationResult("There was an issue authenticating you.");
}
MyContext dbcontext = new MyContext();
var appUser = dbcontext.AppUsers.Where(a => a.ActiveDirectoryLogin.ToLower() == "domain\\" +_userPrincipal.SamAccountName.ToLower()).FirstOrDefault();
if (appUser == null)
{
return new AuthenticationResult("Sorry, you have not been granted user access to the MED application.");
}
// pass both adprincipal and appuser model to build claims identity
_identity = CreateIdentity(_userPrincipal, appUser);
_authenticationManager.SignOut(MyAuthentication.ApplicationCookie);
_authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, _identity);
return new AuthenticationResult();
}
/// <summary>
/// Creates identity and packages into cookie
/// </summary>
/// <param name="userPrincipal"></param>
/// <returns></returns>
private ClaimsIdentity CreateIdentity(UserPrincipal userPrincipal, AppUser appUser)
{
var identity = new ClaimsIdentity(MyAuthentication.ApplicationCookie, ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Active Directory"));
identity.AddClaim(new Claim(ClaimTypes.GivenName, userPrincipal.GivenName));
identity.AddClaim(new Claim(ClaimTypes.Surname, userPrincipal.Surname));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userPrincipal.SamAccountName));
identity.AddClaim(new Claim(ClaimTypes.Name, userPrincipal.SamAccountName));
identity.AddClaim(new Claim(ClaimTypes.Upn, userPrincipal.UserPrincipalName));
if (!String.IsNullOrEmpty(userPrincipal.EmailAddress))
{
identity.AddClaim(new Claim(ClaimTypes.Email, userPrincipal.EmailAddress));
}
// db claims
if (appUser.DefaultAppOfficeId != null)
{
identity.AddClaim(new Claim("DefaultOffice", appUser.AppOffice.OfficeName));
}
if (appUser.CurrentAppOfficeId != null)
{
identity.AddClaim(new Claim("Office", appUser.AppOffice1.OfficeName));
}
var claims = new List<Claim>();
DirectoryEntry dirEntry = (DirectoryEntry)userPrincipal.GetUnderlyingObject();
foreach (string groupDn in dirEntry.Properties["memberOf"])
{
string[] parts = groupDn.Replace("CN=", "").Split(',');
claims.Add(new Claim(ClaimTypes.Role, parts[0]));
}
if (claims.Count > 0)
{
identity.AddClaims(claims);
}
return identity;
}
/// <summary>
/// Authentication result class
/// </summary>
public class AuthenticationResult
{
public AuthenticationResult(string errorMessage = null)
{
ErrorMessage = errorMessage;
}
public String ErrorMessage { get; private set; }
public Boolean IsSuccess => String.IsNullOrEmpty(ErrorMessage);
}
}
}
Эта часть работает нормально. Тем не менее, я должен иметь возможность выдавать себя за ClaimsIdentity при совершении вызовов в базу данных, потому что база данных имеет настройку безопасности на уровне ролей. Мне нужно, чтобы соединение было установлено в контексте ClaimsIdentity до конца сеанса этого пользователя.
- Я установил SPN для Kerberos и знаю, что он работает. Это приложение было
ранее аутентификация Windows с делегированием Kerberos, и она работала правильно.
- Пул приложений работает под учетной записью службы, используемой в имени участника-службы, у которого есть разрешения на делегирование.
- Объект Identity, который я создал, в основном используется только в контексте приложения. Под этим я подразумеваю, что я получаю все необходимые свойства в основном из Active Directory, но из базы данных будет создано два. Этот идентификатор не отображается непосредственно в таблицу SQL или любой другой источник данных.
Может ли кто-нибудь помочь мне привести пример, где я могу олицетворять объект ClaimsIdentity при выполнении запросов базы данных к базе данных SQL Server?