2016-08-12 2 views
1

Я хочу сделать пользовательскую проверку подлинности, потому что у нас много контроллеров, и имеет смысл создавать глобальный фильтр, который применяется для всех контроллеров и их действий, за исключением страницы входа.Пользовательские ASP.NET MVC Forms Authentication

В Global.asax.cs я добавил следующий глобальный фильтр:

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:

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 будет сброшена на каждом запросе. Можно ли установить HttpContext.Current.User со значением Session для каждого запроса? Есть ли другой лучший подход? Что такое лучшая практика? Кроме того, Microsoft, похоже, имеет несколько способов решения этой проблемы (googled много статей об этом, также в stackoverflow Custom Authorization in Asp.net WebApi - what a mess?). В этом есть много путаницы, хотя они разработали новую авторизацию в ядре asp.net.

ответ

3

Одним из возможных подходов является сериализация пользователя как части части UserData файла cookie ASPXAUTH. Таким образом, вам не нужно извлекать его из базы данных по каждому запросу, и вам не нужно использовать сеансы (потому что если вы используете сеансы в веб-ферме, вам придется продолжать этот сеанс где-то, как в базе данных, поэтому вы будете кругооборот к БД в любом случае):

[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

Таким образом, это означает, что все куки сохраненный на стороне клиента (клиентская сторона не отправляет только «идентификатор сеанса», который затем совпадает с сервером, но клиентская сторона отправляет всю информацию)? Я где-то читал, что для размера файла cookie существует ограничение 4 тыс., Но это, конечно, много. Вам обычно нужна только информация, как имя пользователя, идентификатор, культура, IP, ... – broadband

Смежные вопросы