如何在用户点击注销按钮后让用户登录系统并注销?

Lea*_*sed 13 c# asp.net asp.net-mvc owin asp.net-identity

我正在使用microsoft asp.net身份的自定义实现,因为我有自定义表,这就是为什么我已经给出了我的所有方法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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

账户管理员:

[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;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

现在在下面的这行中,我已经给出了7天的到期时间,但是会话在10到15分钟内到期:

  AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);
Run Code Online (Sandbox Code Playgroud)

在我的下面的问题中,你会发现我的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))
                }
            });            
 }
Run Code Online (Sandbox Code Playgroud)

注意:**长时间保持会话活动的原因是因为我的mvc应用程序像Http get调用一样是角度驱动的,Http post调用所以当用户会话过期时我会尝试任何**get或post调用在会话过期的情况下发生,但是当我刷新整个页面时,用户被重定向到登录页面,但直到那时,如果用户不刷新页面,用户将如何知道发生了什么.

tra*_*max 5

你的问题是缺少SecurityStamp. 安全标记是一个随机字符串,用于检查服务器上的密码是否已更改。安全标记存储在 cookie 中,并且时不时地根据数据库进行检查。如果数据库(商店)中的值与 cookie 中的值不同 - 要求用户登录。SecurityStampValidator正在做所有的检查和 cookie 失效。

您正在为用户使用自定义存储,这很好,但是您的存储没有实现,IUserSecurityStampStore并且当用户登录时 cookie 没有获得SecurityStamp. 这会导致 的故障SecurityStampValidator

所以你的选择是:

  1. IUserSecurityStampStore在您的商店中实施。
  2. SecurityStampValidator从您的配置中删除。

由于安全问题,我不喜欢第二种选择。您希望您的用户永远保持登录状态 - 这意味着 cookie 永远不会失效。但是当用户有 2 个浏览器时,都登录了。并在其中一个浏览器中更改密码 - 第二个应该注销并要求输入密码。如果不检查安全标记,第二个浏览器将不会注销,cookie 仍然有效。现在想象第二个浏览器在公共计算机上打开并且用户忘记注销 - 即使更改密码也无法结束该会话。

IUserSecurityStampStore合同上实施外观

/// <summary>
///     Stores a user's 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);
}
Run Code Online (Sandbox Code Playgroud)

基本上这会向您的用户表添加另一列:SecurityStamp并且您需要在那里保存一个字符串。邮票的值是任何随机字符串。默认身份实现(第 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意 - 在此操作中您不需要将用户保存到存储中。UserManager正在为您执行此操作UpdateSecurityStampAsync- 除非您自己覆盖此方法。

另外不要忘记SecurityStamp在创建新用户时为字段赋值。并使用值更新所有现有用户。像这样的东西会起作用update MyUsersTable set SecurityStamp = convert(nvarchar(38), NewId())