Как сохранить вход пользователя в систему и выйти из системы только после того, как пользователь нажимает кнопку выхода из системы?
Я использую пользовательскую реализацию идентификатора asp.net для microsoft, потому что у меня есть пользовательские таблицы, поэтому я дал пользовательскую реализацию всех моих методов IUserStore и IUserPasswordStore.
Проблема заключается в том, что пользователь регистрируется после 10 - 15 минут входа пользователя сеанс истек, но я хочу, если пользователь не выйдет из системы, я хочу войдите в систему в систему.
Код:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
}
}
Контроллер учетной записи:
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<UserModel>(new UserStore()))
{
}
public AccountController(UserManager<UserModel> userManager)
{
UserManager = userManager;
}
public UserManager<UserModel> UserManager { get; private set; }
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(string email, string password, bool rememberMe = false, string returnUrl = null)
{
if (ModelState.IsValid)
{
var user = UserManager.Find(email, password);
if (user != null)
{
await SignInAsync(user, rememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View();
}
private async Task SignInAsync(UserModel user, bool isPersistent)
{
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim("FullName", user.FirstName + " " + user.LastName));
identity.AddClaim(new Claim("Email", user.Email));
identity.AddClaim(new Claim("Role", user.Role));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
}
Web.config:
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
Теперь в этой ниже строке я дал 7 дней истечения срока действия, но все же сеансы истекают через 10 - 15 минут:
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);
Здесь в моем следующем вопросе вы найдете мой UserModel, пользовательский класс UserStore, но для того, чтобы этот вопрос был небольшим, я не помещаю этот код здесь:
UserModel и UserStore
Обновление. Я полностью исключил класс ApplicationUser, поэтому теперь код ниже для меня бесполезен, и я думаю, из-за этого мой файл cookie истек, я думаю (все же я не уверен):
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
}
Примечание. ** Причина поддержания активного сеанса в течение длительного времени заключается в том, что мое приложение mvc angular управляется как вызов Http get, Http post call, поэтому, когда пользовательский сеанс истек, и я, если я попытаюсь ** get или post, тогда ничего не происходит в случае окончания сеанса, но когда я обновляю всю свою страницу, тогда пользователь перенаправляется на страницу входа, но до тех пор, как пользователь узнает, что происходит, если пользователь не обновляет страницу.
Ответы
Ответ 1
Ваша проблема связана с отсутствием SecurityStamp
. Марка безопасности - это случайная строка, которая работает как проверка, был ли пароль изменен на сервере. Штамп безопасности хранится в файле cookie, а затем - и затем проверяется на базе базы данных. Если значение в базе данных (хранилище) отличается от значения в файле cookie - пользователю предлагается войти в систему. SecurityStampValidator
выполняет все проверки и недействительность файлов cookie.
Вы используете пользовательское хранилище для пользователей и это прекрасно, но ваше хранилище не реализует IUserSecurityStampStore
, и когда пользовательский файл cookie не получает значение SecurityStamp
. Это приводит к неисправности SecurityStampValidator
.
Итак, ваши варианты:
- Внесите
IUserSecurityStampStore
в ваш магазин.
- Удалите
SecurityStampValidator
из вашей конфигурации.
Мне не нравится второй вариант из-за проблемы с безопасностью. Вы хотите, чтобы ваши пользователи оставались включенными навсегда - это означает, что cookie никогда не аннулируется. Но когда у пользователя есть 2 браузера, оба вошли в систему. И сменить пароль в одном из браузеров - второй должен быть выведен из системы и просить пароль. Не проверяя маркер безопасности, второй браузер не будет выходить из системы, и файл cookie будет по-прежнему действителен. Теперь представьте, что второй браузер открывается на общедоступном компьютере, и пользователь забыл выйти из системы - никоим образом не завершить этот сеанс даже с изменением пароля.
Чтобы реализовать IUserSecurityStampStore
посмотреть на контракт:
/// <summary>
/// Stores a user security stamp
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TKey"></typeparam>
public interface IUserSecurityStampStore<TUser, in TKey> : IUserStore<TUser, TKey> where TUser : class, IUser<TKey>
{
/// <summary>
/// Set the security stamp for the user
/// </summary>
/// <param name="user"></param>
/// <param name="stamp"></param>
/// <returns></returns>
Task SetSecurityStampAsync(TUser user, string stamp);
/// <summary>
/// Get the user security stamp
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
Task<string> GetSecurityStampAsync(TUser user);
}
В основном это добавляет другой столбец в таблицу ваших пользователей: SecurityStamp
и вам нужно сохранить там строку. И значение для штампа - любая случайная строка. Идентификация по умолчанию Identity (вокруг строки 734) использует Guid.NewGuid().ToString()
- я предлагаю вам сделать то же самое.
Магазин вашего пользователя будет выглядеть примерно так:
public class UserStore : IUserStore<UserModel>, IUserPasswordStore<UserModel>, IUserSecurityStampStore<TUser>
{
// your other methods
public async Task SetSecurityStampAsync(TUser user, string stamp)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
Task<string> GetSecurityStampAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.SecurityStamp);
}
}
Имейте в виду - вам не нужно сохранять пользователя в хранилище в этой операции. UserManager
делает это для вас в UpdateSecurityStampAsync
- если вы не переопределите этот метод самостоятельно.
Также не забудьте назначить значение в поле SecurityStamp
при создании новых пользователей. И обновите всех существующих пользователей со значением. Что-то вроде этого будет работать update MyUsersTable set SecurityStamp = convert(nvarchar(38), NewId())
Ответ 2
метод управления вызовом в определенный интервал времени, поэтому он будет reset тайм-аутом сеанса при вызове evry. Например, если изначально вы установили свой тайм-аут сеанса через 30 минут, а через 20 минут вы вызываете это действие, он снова будет reset тайм-аута сеанса снова 30 минут, таким образом ваш сеанс остается активным даже до 30 минут после входа в систему.
Поместите код JQuery в макет
JQuery
var RefreshSessionInterval;
$(document).ready(function () {
clearInterval(RefreshSessionInterval);
RefreshSessionInterval = setInterval("RefreshSession()", 30000); // change your interval time as per requirement
});
function RefreshSession() {
$.ajax({
type: "POST",
url: '@Url.Action("RefreshSession", "YourControllerName")',
success: function (data) {
},
error: function () {
}
});
}
Контроллер:
Public void RefreshSession()
{
//your session reset from this line, as i know you don't have to write any code here.
}
public bool LogOut()
{
LogOff();
return true;
}
void LogOut()
{
Session.Clear();
Session.Abandon();
Session.RemoveAll();
ClearCache();
}
void ClearCache()
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
Response.Cache.SetNoStore();
////FormsAuthentication.SignOut();
}
Ответ 3
У меня была такая же проблема, и я был очень смущен, потому что без какой-либо причины пользователь был перенаправлен на страницу входа в систему, что он не был авторизован. Я изменил таймаут на более чем 8 часов, но ничего не изменилось. После прочтения многих страниц, таких как неожиданный выход из системы Aspnet или frequent-unexpected-user-logoff, я обнаружил, что с машиной что-то не так и после проверки машинного ключа в файле web.config я мог обнаружить проблему с помощью машинного ключа. Изменяя машинный ключ и делайте его одинаковым с другими в разделе Owin, все работает хорошо.
Ответ 4
Вы пробовали
ExpireTimeSpan = TimeSpan.FromDays(7);
поэтому это сделает ваш код:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
ExpireTimeSpan = TimeSpan.FromDays(7);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
}
}
Ответ 5
Вы также должны настроить тайм-аут сеанса на уровне пула приложений в IIS, как описано здесь: https://technet.microsoft.com/en-us/library/cc771956(v=ws.10).aspx
Ответ 6
Вот что я сделал, когда я закодировал пользователя, чтобы он был подписан...
код
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
Контроллер учетных записей
public class AccountController : Controller
{
/// <summary>
/// Initializes a new instance of the <see cref="AccountController"/> class.
/// </summary>
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AccountController"/> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
/// <summary>
/// Gets the user manager.
/// </summary>
/// <value>
/// The user manager.
/// </value>
public UserManager<ApplicationUser> UserManager { get; private set; }
//
// GET: /Account/Login
/// <summary>
/// Logins the specified return URL.
/// </summary>
/// <param name="returnUrl">The return URL.</param>
/// <returns></returns>
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
/// <summary>
/// Logins the specified model.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="returnUrl">The return URL.</param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
ИЛИ..
Вы также можете настроить тайм-аут сеанса для пользователя на уровне пула приложений в IIS.
Ответ 7
Время жизни сеанса (до тех пор, пока сеанс не исчезнет) и время ожидания проверки подлинности (как долго, пока пользователь не сможет снова войти в систему) - это два отдельных и отдельных таймфрейма.
Если время ожидания аутентификации больше, чем время сеанса, это означает, что сеанс начнется с уже прошедшего проверку пользователя (то есть пользователю не нужно будет входить в систему для запуска сеанса).
Если срок службы аутентификации короче таймфрейма сеанса, это означает, что пользователь будет вынужден войти в систему до истечения срока их сеанса. Я не уверен, что сеанс "обновляется", когда пользователь повторно проверяет (возможно, предположительно).
Простое установление очень длительных выходов для сеанса и аутентификации может не быть готовым решением для пиратской продукции (т.е. существует множество способов "исчезновения сеансов" ).
Почему вас беспокоит, если пользовательский сеанс пропадает, а затем запускается новый (без входа в систему)? Без дополнительной информации о том, что вы намереваетесь, я не могу понять суть вашего вопроса.
Ответ 8
Изучите настройки элемента в элементе аутентификации вашего файла web.config.
Обратите внимание на значения по умолчанию для двух применимых параметров.
- таймаут (по умолчанию 30 минут)
- slideExpiration (True или False/default varines с версией .NET Framework)
В вашей ситуации вам, вероятно, понадобится продолжительность таймаута намного выше 30 минут и значение slideExpiration True.