2

asp.net MVC アプリケーションにカスタム フォーム認証を使用していますが、Cookie を持っていないように見える一部のユーザーに問題があります。使用しているカスタム フォーム認証方法は、このカスタム フォーム認証に似ています。基本的に、カスタム プリンシパルと ID を作成し、シリアル化し、 FormsAuthenticationTicket の UserData プロパティに格納します。

ログイン

MyCustomPrincipal principal = new MyCustomPrincipal(user);
DateTime expiration = DateTime.Now.AddMinutes(30);

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                1,
                u.Username,
                DateTime.Now,
                expiration,
                true,
                JsonConvert.SerializeObject(principal));

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
cookie.Expires = expiration;

Response.Cookies.Set(cookie);

次に、global.asax の Application_AuthenticateRequest イベントで認証 Cookie を取得します。

global.asax - Application_AuthenticateRequest

// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];

// If the cookie can't be found, don't issue the ticket
if (authCookie == null) return;

// Get the authentication ticket and rebuild the principal
// & identity
FormsAuthenticationTicket authTicket =
          FormsAuthentication.Decrypt(authCookie.Value);

MyCustomPrincipal userPrincipal = new MyCustomPrincipal(authTicket.UserData);
DateTime expiration = DateTime.Now.AddMinutes(30);

FormsAuthenticationTicket newAuthTicket = new FormsAuthenticationTicket(
              1,
              ((MyCustomIdentity)userPrincipal.Identity).Username,
              DateTime.Now,
              expiration,
              true,
              JsonConvert.SerializeObject(userPrincipal));



authCookie.Value = FormsAuthentication.Encrypt(newAuthTicket);
authCookie.Expires = expiration;

HttpContext.Current.Response.Cookies.Set(authCookie);

Context.User = userPrincipal;

web.config

<authentication mode="Forms">
  <forms loginUrl="~/Home/Index" timeout="29" name="MYFORMSAUTH" cookieless="UseCookies"/>
</authentication>

これは大多数のユーザーにとっては問題なく機能しますが、認証 Cookie が設定されていないように見えるユーザーもいます。Elmah エラー ログにさらに情報を追加するためにいくつかのテストを行って、問題についてさらに詳しい情報が得られるかどうかを確認しました。

まず、Login メソッドで authcookie を設定する前後に、いくつかのテスト Cookie を設定してみました。これらの Cookie はElmah のログに記録されていないため、この方法で任意の種類の Cookie を追加しても機能していないようです。ただし、ASP.NET_SessionId、Google アナリティクス Cookie など、ログには他の Cookie があり、アプリケーションの他の場所に設定した他の Cookie もある場合があります (おそらく以前のセッションから)。

次に、ログイン アクションからセッションに情報を追加し、次のアクションで authcookie が見つからない場合はエラー ログに含めてみました。Cookie の長さ (名前の長さ + 暗号化された値の長さ) と、ログイン試行の時刻を含めました。これらは両方とも、ユーザーの資格情報が有効で、アプリケーションが認証 Cookie の追加を試みる場合にのみ追加されます。生成されるエラー ログにこれらの値が表示されます長さは常に 0 より大きく、約 2300 より大きいものは見たことがないので、サイズは問題ではないと思います。また、試行されたログインは、エラーが発生した時間と同じです。したがって、セッション変数は、エラーが発生する直前に設定されているはずです (Cookie が失われました)。

さらにいくつかのメモ-

  • 特にエラーを引き起こしていると思われるブラウザはないようです (ただし、モバイル ブラウザでより多く発生する可能性があります)。
  • 繰り返しますが、このサイトは大多数のユーザーで機能しているように見えますが、もちろん問題を再現することはできません
  • テスト Cookie が表示されていないため、何らかの理由でその時点でログイン メソッドから Cookie が設定されていないと推測しています (ただし、以前の成功したログインを意味する別の場所に設定された他の Cookie を確認できます)。
  • elmah ログの http リファラーは、通常、ログイン ページに設定されています。これは、ユーザーがログインせずに問題のあるページにアクセスしていない可能性が高いことを意味します (少なくとも一部の時間)。セッション変数の状態は、その仮定をサポートしているようです。
  • これらのエラーが連続して (1 分ほど間隔を空けて) 表示されることがよくあります。これは、ログイン試行を繰り返しても問題が解決されないことを意味します (理由がわからない)。
  • この問題を抱えているユーザーは、引き続き問題を抱えているようです。つまり、「くじ引きの運」ではないように見えますが、ユーザーのアカウント (Cookie の長さのセッション変数は、正しくシリアル化されていることを意味します) またはクライアント ブラウザーのいずれかに問題があります。
  • モバイル デバイスではログインできたが、デスクトップではログインできなかったユーザーが少なくとも 1 人いると聞きました。
  • 合計で、サイトはおそらく 10 個ほどの Cookie (追加されたさまざまなテスト Cookie をすべて含む) を使用します。テスト Cookie を追加する前は、認証 Cookie を含めて約 4 個使用していました。また、バグが発生した場合、通常、リクエストには 2 つまたは 3 つの Cookie しかないように見えるため、Cookie の数は問題ではないと思います。

この時点で、私はほとんど何でも試してみたいと思っています。セッションに保存されているカスタム ID をバックアップとして使用してセットアップを試みましたが、それを機能させることができませんでした。削除できます。)

テキストの壁で申し訳ありませんが、私たちが調査し、おそらく除外した可能性のあるすべての問題を指摘したかっただけです.

編集

別の潜在的に関連する問題があるようです。一部の ID の「IsAuthenticated」属性が false に設定されているべきではないのに、false に設定されていると思わせるエラー ログが表示されます。これを false に初期化し、ユーザーがセキュリティの質問に答えた後に true に設定します。true に設定すると、原則と認証チケット/Cookie が更新されます。カスタムプリンシパルを逆シリアル化する方法に問題があるためにこれが発生しているかどうかはわかりません。

4

2 に答える 2

0

だから私はちょっとあきらめて、セッションを使用してプリンシパルを保存し、認証 Cookie が表示されないときに確認することにしました。カスタム Authorize 属性を作成し、そこでセッションをチェックすることで、これをいくらか簡単に行うことができます。これをまだ本番環境にプッシュしていないため、これで問題が回避されるとは 100% 確信が持てませんが、予備テストでは十分であることが示唆されています。

CustomAuthorizeAttribute

public class MyCustomAuthorizeAttribute : AuthorizeAttribute
{

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Get the authentication cookie
        string cookieName = FormsAuthentication.FormsCookieName;
        HttpCookie authCookie = HttpContext.Current.Request.Cookies[cookieName];

        // If the cookie can be found, use the base authentication
        if (authCookie != null)
        {
            base.OnAuthorization(filterContext);
        }
        else
        {
            // The cookie is not found, check the session for the principal

            var p = HttpContext.Current.Session[FormsAuthentication.FormsCookieName];

            if (p != null)
            {
                // there is a principal object in the session
                MyCustomPrincipal principal = (MyCustomPrincipal)p;

                HttpContext.Current.User = principal;                   

                // we've loaded the principal, now just do the base authorization
                base.OnAuthorization(filterContext);

            }
            else
            {
                // there is no principal object in the cookie or the session, the user is not authenticated
                HandleUnauthorizedRequest(filterContext);
            }

        }
    }     
}

カスタム認証属性を使用して現在のプリンシパルを適切に設定したら、ベース認証を使用するだけでよいため、その機能を自分で実装することを心配する必要はありません。基本承認は、現在のプリンシパルを確認し、それに基づいて承認する必要があります。

根本的な問題を実際に解決するわけではないため、これを回答としてマークするつもりはありませんが、他の誰かが同様の問題に遭遇した場合に備えて、潜在的な回避策として提供すると思いました.

于 2014-04-29T20:38:29.360 に答える