web-dev-qa-db-ja.com

ユーザーがシステムにログインし続け、ユーザーがログアウトボタンをクリックした後にのみログアウトする方法は?

私はMicrosoftasp.net IDのカスタム実装を使用しています。これは、カスタムテーブルがあるため、すべてのメソッド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、custom UserStoreクラスが見つかりますが、この質問を小さく保つために、ここにそのコードを配置していません:

serModelおよびUserStore

UpdateApplicationUserクラスを完全に除外したので、以下のコードは私には役に立たず、これが原因で私のクッキーが期限切れになると思います(それでも私にはわかりません):

 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 call、Http post call so whenユーザーセッションが期限切れになり、** getまたはpost呼び出しを試行すると、セッションが期限切れになっても何も起こりませんが、ページ全体を更新すると、ユーザーはログインページにリダイレクトされますが、それまではユーザーがページを更新しない場合に何が起こっているかをユーザーがどのように知るか.

あなたの問題はSecurityStampの欠如にあります。セキュリティスタンプは、サーバーでパスワードが変更されたかどうかを確認するために機能するランダムな文字列です。セキュリティスタンプはCookieに保存され、データベースと照合されます。データベース(ストア)の値がCookieの値と異なる場合、ユーザーはログインを求められます。 SecurityStampValidatorは、すべてのチェックとCookieの無効化を行っています。

ユーザー用のカスタムストレージを使用していますが、それは問題ありませんが、ストレージはIUserSecurityStampStoreを実装しておらず、ユーザーのログインCookieがSecurityStampの値を取得していません。これにより、SecurityStampValidatorが誤動作します。

だからあなたのオプションは:

  1. ストアにIUserSecurityStampStoreを実装します。
  2. 構成からSecurityStampValidatorを削除します。

セキュリティ上の問題から、2番目のオプションは好きではありません。ユーザーが永久にログインしたままにする必要があります。つまり、Cookieが無効になることはありません。ただし、ユーザーが2つのブラウザを使用している場合は、両方がログインしています。そして、いずれかのブラウザでパスワードを変更します-2番目はログアウトしてパスワードを要求する必要があります。セキュリティスタンプを確認しないと、2番目のブラウザはログアウトされず、Cookieは引き続き有効です。次に、2番目のブラウザが公共のコンピュータで開かれ、ユーザーがログアウトするのを忘れたと想像してください。パスワードを変更しても、セッションを終了する方法はありません。

IUserSecurityStampStoreを実装するには、 contract: を調べます

_/// <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);
}
_

基本的に、これによりユーザーテーブルにSecurityStampという別の列が追加され、そこに文字列を保存する必要があります。また、スタンプの値は任意のランダムな文字列です。 Default Identity implmenetation(around 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())

3
trailmax

私は同じ問題を抱えていましたが、何の理由もなくユーザーがログインページにリダイレクトされたことは、彼が承認されなかったことを意味するため、本当に混乱しました。タイムアウトを8時間以上に変更しましたが、何も変更されていません。 Aspnet予期しないログアウト または frequency-unexpected-user-logoff などの多くのページを読んだ後、web.configでマシンキーを確認した後、マシンキーに問題があることがわかりましたファイルマシンキーの問題を検出できました。マシンキーを変更してOwinセクションの他のユーザーと同じにすることで、すべてがうまく機能しています。

3
Mohsen Shadman

やってみました

 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));
        }
    }
3
ShufflerShark

ここで説明するように、IIS)のアプリケーションプールレベルでセッションタイムアウトも構成する必要があります。 https://technet.Microsoft.com/en-us/library/cc771956 (v = ws.10).aspx

2
Trifon

特定の時間間隔でコントローラーメソッドを呼び出すしたがって、evry呼び出しでセッションタイムアウトがリセットされます。たとえば、最初にセッションタイムアウトを30分に設定し、20分後にこのアクションを呼び出すと、セッションタイムアウトが再び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();
}

サインインを維持するようにユーザーをコーディングしたときに行ったことは次のとおりです...

コード

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のアプリケーションプールレベルでユーザーのセッションタイムアウトを構成することもできます。

1
Yaman Ahlawat

セッションの有効期間(セッションが消えるまでの期間)と認証の有効期間(ユーザーが再度ログインする必要があるまでの期間)は、2つの別個の異なる時間枠です。

認証の有効期間がセッションの時間枠よりも長い場合、これは、既に認証されたユーザーでセッションが開始されることを意味します(つまり、ユーザーはセッションを開始するためにログインする必要はありません)。

認証の有効期間がセッションの時間枠よりも短い場合、これは、セッションが期限切れになる前にユーザーがログインを強制されることを意味します。ユーザーが再認証したときにセッションが「更新」されたかどうかはわかりません(推測では...おそらく)。

セッションと認証に非常に長い有効期限を設定するだけでは、海賊版の本番環境に対応したソリューションではない可能性があります(つまり、セッションを「消滅」させる方法はたくさんあります)。

ユーザーのセッションが消えてから新しいセッションが開始された場合(ユーザーがログインしなくても)、なぜ気にするのですか?あなたが何を意図しているのかについてもう少し情報がなければ、私はあなたの質問の核心を本当に理解することはできません。

0
user2845090

Web.configファイルの authentication element 内の forms element の設定を調べます。

該当する2つの設定のデフォルト値に注意してください。

  1. タイムアウト(デフォルトは30分)
  2. slideingExpiration(TrueまたはFalse/.NET Frameworkバージョンのデフォルトバリアン)

状況によっては、タイムアウト期間を30分よりはるかに長くし、slidingExpiration値をTrueにする必要があります。

0
JohnH