2016-08-12 17 views
1

私たちには多数のコントローラがあり、ログインページ以外のすべてのコントローラとそのアクションに適用されるグローバルフィルタを作成するのが理想的です。カスタムASP.NET MVCフォーム認証

public class Global : HttpApplication 
{ 
    void Application_Start(object sender, EventArgs e) // Code that runs on application startup 
    { 
    ... // only showing important part 
    GlobalFilters.Filters.Add(new Filters.AuthenticationUserActionFilter()); 
    ... 
} 

ファイルAuthenticationUserActionFilter.cs

はGlobal.asax.csでは、私は次のグローバルフィルタを追加

public class AuthorizeUserActionFilter : System.Web.Mvc.Filters.IAuthenticationFilter 
{ 
    public void OnAuthentication(AuthenticationContext filterContext) 
    { 
    bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousActionFilter), inherit: true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousActionFilter), inherit: true); 

    if (skipAuthorization) // anonymous filter attribute in front of controller or controller method 
     return; 

    // does this always read value from ASPXAUTH cookie ? 
    bool userAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated; 

    if (!userAuthenticated) 
    { 
    filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary() { { "controller", "Account" }, { "action", "Login" } }); 
    return; 
    } 

    if(HttpContext.Current.User as Contracts.IUser == null) 
    { 
    // check if IUser is stored in session otherwise retrieve from db 
    // System.Web.HttpContext.Current.User is reseted on every request. 
    // Is it ok to set it from Session on every request? Is there any other better approach? 
    if (HttpContext.Current.Session["User"] != null && HttpContext.Current.Session["User"] as Contracts.IUser != null) 
    { 
     HttpContext.Current.User = HttpContext.Current.Session["User"] as Contracts.IUser; 
    } 
    else 
    { 
     var service = new LoginService(); 
     Contracts.ISer user = service.GetUser(filterContext.HttpContext.User.Identity.Name); 

     HttpContext.Current.Session["User"] = user; 
     HttpContext.Current.User = user; 
    } 
    } 
} 

public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) {} 

を}

私のログインコードは次のようである(AccountController.cs中) :

[Filters.AllowAnonymousActionFilter] 
[HttpPost] 
public JsonResult Login(string username, string password, bool rememberMe = false) 
{ 
    LoginService service = new LoginService(); 
    Contracts.IUser user = service .Login(username, password); 

    System.Web.HttpContext.Current.Session["User"] = value; 
    System.Web.HttpContext.Current.User = value; 

    // set cookie i.e. ASPX_AUTH, if remember me, make cookie persistent, even if user closed browser 
    if (System.Web.Security.FormsAuthentication.IsEnabled) 
     System.Web.Security.FormsAuthentication.SetAuthCookie(username, rememberMe); 

    return new SuccessResponseMessage().AsJsonNetResult(); 
} 

Contracts.IUserインタフェース:

public interface IUser : IPrincipal 
    { 
    Contracts.IUserInfo UserInfo { get; } 
    Contracts.ICultureInfo UserCulture { get; } 
    } 

私の質問はこれです:

System.Web.HttpContext.Current.Userは、すべての要求にresetedされます。要求ごとにセッション値をHttpContext.Current.Userに設定してもよろしいですか?他のより良いアプローチはありますか?ベストプラクティスとは何ですか?また、マイクロソフトでは、この問題に対処するための複数の方法があるようだ(stackoverflow Custom Authorization in Asp.net WebApi - what a mess?もこれに関する多くの記事をgoogled)。これについて多くの混乱がありますが、asp.netコアで新しいAuthorizationを開発しました。

答えて

3

可能なアプローチの1つは、ASPXAUTHクッキーのUserData部分の一部としてユーザーをシリアル化することです。この方法では、リクエストごとにデータベースからフェッチする必要はなく、セッションを使用する必要はありません(Webファームでセッションを使用する場合は、データベースのようにこのセッションを維持する必要があるため

[Filters.AllowAnonymousActionFilter] 
[HttpPost] 
public JsonResult Login(string username, string password, bool rememberMe = false) 
{ 
    LoginService service = new LoginService(); 
    Contracts.IUser user = service.Login(username, password); 

    string userData = Serialize(user); // Up to you to write this Serialize method 
    var ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddHours(24), rememberMe, userData); 
    string encryptedTicket = FormsAuthentication.Encrypt(ticket); 
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)); 

    return new SuccessResponseMessage().AsJsonNetResult(); 
} 

そしてカスタム認証フィルタであなたは、チケットを復号化してユーザーを認証できます:あなたは、ラウンドトリップ)とにかくデシベルになります

public void OnAuthentication(AuthenticationContext filterContext) 
{ 
    ... your stuff about the AllowAnonymousActionFilter comes here 

    var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; 
    if (authCookie == null) 
    { 
     // Unauthorized 
     filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary() { { "controller", "Account" }, { "action", "Login" } }); 
     return; 
    } 

    // Get the forms authentication ticket. 
    var authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
    Contracts.ISer user = Deserialize(authTicket.UserData); // Up to you to write this Deserialize method -> it should be the reverse of what you did in your Login action 

    filterContext.HttpContext.User = user; 
} 
+0

だから、これは全体のクッキーがあることを意味し、 (クライアント側は 'session id'だけを送信し、サーバはそれと一致しますが、クライアント側はすべての情報を送信します)?私はどこかでクッキークライアントのサイズに4kの制限がありますが、それはもちろん大変です。あなたは通常、ユーザー名、ID、文化、IPのような情報が必要です... – broadband

関連する問題