Динамическое подключение к базе данных с использованием удостоверения Asp.Net - PullRequest
0 голосов
/ 12 марта 2019

Я работаю над мультитенантным приложением, которое использует несколько баз данных.Существует одна основная база данных, которая содержит информацию о пользователях, и затем каждая база данных арендаторов также имеет своих собственных пользователей для этого арендатора (которые являются подмножеством пользователей в основной базе данных).

Пользователь войдет в систему, которая проверитОсновная база данных, затем на основе их данных (то есть, к какому арендатору они принадлежат) будет регистрировать их в приложении, используя данные пользователя в своей базе данных арендаторов.

Я использую метод, описанный в этой теме ( Динамическое соединение с базой данных с использованием Asp.net MVC и Identity2 ) для установки базы данных для UserManager каждый раз, потому что в момент запуска приложения оно не будет знать, какую базу данных использовать, поэтому следующий код в «Startup.Auth»будет установка неверной базы данных:

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

Это, кажется, работает хорошо для большинства вещей, но у меня есть одна проблема - пользователь выходит из системы по истечении времени, установленного в «validateInterval», показанном в коде ниже(для тестирования установлено 20 секунд):

 app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromSeconds(20),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),                        
                OnApplyRedirect = ctx =>
                {
                    if (!IsAjaxRequest(ctx.Request))
                    {
                        ctx.Response.Redirect(ctx.RedirectUri);
                    }
                }
            }
        });

Я думаю, что проблема может заключаться в том, что когда приведенный выше код вызывается в файле «Startup.Auth», он не знает, какую базу данных использовать, однако я не подтвердил это.

Если я отлаживаю код «GenerateUserIdentityAsync», я вижу, что он получает правильный «securityStamp» для пользователя из клиентской базы данных, что заставляет меня думать, что он находит правильную базу данных, но я не могу понять, почему он все еще выходит из системы пользователяпо истечении времени, установленного для «validateInterval».

Может кто-нибудь предложить какой-либо совет о том, как это можно решить, или, по крайней мере, о возможных способах отладки и устранения проблемы?

Ответы [ 2 ]

0 голосов
/ 16 марта 2019

Хорошо, это полное решение, которое я придумала, которое частично использует то, что предложил @jacktric, но также позволяет проверять штамп безопасности, если пароль пользователя был изменен в другом месте. Пожалуйста, дайте мне знать, если кто-то может порекомендовать какие-либо улучшения или увидеть какие-либо недостатки в моем решении.

Я удалил раздел OnValidateIdentity из раздела UseCookieAuthentication следующим образом:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        OnApplyRedirect = ctx =>
        {
            if (!IsAjaxRequest(ctx.Request))
            {
                ctx.Response.Redirect(ctx.RedirectUri);
            }
        }
    }
});

Затем у меня есть следующий IActionFilter, который зарегистрирован в FilterConfig.cs, который проверяет, вошел ли пользователь в систему (у меня есть части системы, к которым могут обращаться анонимные пользователи) и совпадает ли текущая метка безопасности с база данных. Эта проверка выполняется каждые 30 минут с использованием сеансов, чтобы выяснить, когда была последняя проверка.

public class CheckAuthenticationFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {

    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        try
        {
            // If not a child action, not an ajax request, not a RedirectResult and not a PartialViewResult
            if (!filterContext.IsChildAction
                && !filterContext.HttpContext.Request.IsAjaxRequest()
                && !(filterContext.Result is RedirectResult)
                && !(filterContext.Result is PartialViewResult))
            {
                // Get current ID
                string currentUserId = filterContext.HttpContext.User.Identity.GetUserId();

                // If current user ID exists (i.e. it is not an anonymous function)
                if (!String.IsNullOrEmpty(currentUserId))
                {
                    // Variables
                    var lastValidateIdentityCheck = DateTime.MinValue;
                    var validateInterval = TimeSpan.FromMinutes(30);
                    var securityStampValid = true;

                    // Get instance of userManager   
                    filterContext.HttpContext.GetOwinContext().Get<DbContext>().Database.Connection.ConnectionString = DbContext.GetConnectionString();
                    var userManager = filterContext.HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();

                    // Find current user by ID
                    var currentUser = userManager.FindById(currentUserId);

                    // If "LastValidateIdentityCheck" session exists
                    if (HttpContext.Current.Session["LastValidateIdentityCheck"] != null)
                        DateTime.TryParse(HttpContext.Current.Session["LastValidateIdentityCheck"].ToString(), out lastValidateIdentityCheck);

                    // If first validation or validateInterval has passed
                    if (lastValidateIdentityCheck == DateTime.MinValue || DateTime.Now > lastValidateIdentityCheck.Add(validateInterval))
                    {
                        // Get current security stamp from logged in user
                        var currentSecurityStamp = filterContext.HttpContext.User.GetClaimValue("AspNet.Identity.SecurityStamp");

                        // Set whether security stamp valid
                        securityStampValid = currentUser != null && currentUser.SecurityStamp == currentSecurityStamp;

                        // Set LastValidateIdentityCheck session variable
                        HttpContext.Current.Session["LastValidateIdentityCheck"] = DateTime.Now;
                    }

                    // If current user doesn't exist or security stamp invalid then log them off 
                    if (currentUser == null || !securityStampValid)
                    {
                        filterContext.Result = new RedirectToRouteResult(
                                new RouteValueDictionary { { "Controller", "Account" }, { "Action", "LogOff" }, { "Area", "" } });
                    }
                }
            }
        }
        catch (Exception ex)
        {
            // Log error                
        }
    }
}

У меня есть следующие методы расширения для получения и обновления заявок для вошедшего в систему пользователя (взято из этого поста https://stackoverflow.com/a/32112002/1806809):

public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value)
{
    var identity = currentPrincipal.Identity as ClaimsIdentity;
    if (identity == null)
        return;

    // Check for existing claim and remove it
    var existingClaim = identity.FindFirst(key);
    if (existingClaim != null)
        identity.RemoveClaim(existingClaim);

    // Add new claim
    identity.AddClaim(new Claim(key, value));

    // Set connection string - this overrides the default connection string set 
    // on "app.CreatePerOwinContext(DbContext.Create)" in "Startup.Auth.cs"
    HttpContext.Current.GetOwinContext().Get<DbContext>().Database.Connection.ConnectionString = DbContext.GetConnectionString();
    var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
    authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
}

public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
{
    var identity = currentPrincipal.Identity as ClaimsIdentity;
    if (identity == null)
        return null;

    var claim = identity.Claims.FirstOrDefault(c => c.Type == key);
    return claim.Value;
}

И, наконец, везде, где обновляется пароль пользователя, я вызываю следующее, это обновляет штамп безопасности для пользователя, чей пароль редактируется, и если это текущий пароль пользователя, который редактируется, то он обновляет утверждение securityStamp. для текущего пользователя, чтобы он не вышел из своего текущего сеанса в следующий раз, когда будет выполнена проверка достоверности:

// Update security stamp
UserManager.UpdateSecurityStamp(user.Id);

// If updating own password
if (GetCurrentUserId() == user.Id)
{
    // Find current user by ID
    var currentUser = UserManager.FindById(user.Id);

    // Update logged in user security stamp (this is so their security stamp matches and they are not signed out the next time validity check is made in CheckAuthenticationFilter.cs)
    User.AddUpdateClaim("AspNet.Identity.SecurityStamp", currentUser.SecurityStamp);
}
0 голосов
/ 14 марта 2019

У меня возникла та же проблема с моим многопользовательским приложением ASP.NET MVC. Если ваша цель - установить срок действия для вошедшего в систему пользователя, просто удалите код в CookieAuthenticationProvider и задайте свойство ExpireTimeSpan в родительском CookieAuthenticationOptions.

Ваш код должен быть:

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            ExpireTimeSpan = TimeSpan.FromMinutes(15), //cookie expiration after 15 mins of user inactivity
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
            }
        });

Надеюсь, это поможет.

...