【问题标题】:Custom ASP.NET MVC Forms Authentication自定义 ASP.NET MVC 表单身份验证
【发布时间】:2016-08-12 09:18:35
【问题描述】:

我想进行自定义身份验证,因为我们有许多控制器,因此创建适用于所有控制器及其操作的全局过滤器很有意义,登录页面除外。

在 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.I用户界面:

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

我的问题是这样的:

System.Web.HttpContext.Current.User 在每次请求时都会重置。可以在每个请求上使用 Session 值设置 HttpContext.Current.User 吗?还有其他更好的方法吗?什么是最佳做法?此外,微软似乎有多种方法来处理这个问题(谷歌搜索了很多关于这个的文章,也在 stackoverflow Custom Authorization in Asp.net WebApi - what a mess? 上)。尽管他们在 asp.net core 中开发了新的授权,但对此有很多困惑。

【问题讨论】:

    标签: c# asp.net asp.net-mvc-5 authorization


    【解决方案1】:

    一种可能的方法是将用户序列化为ASPXAUTH cookie 的UserData 部分的一部分。这样你就不需要在每次请求时从数据库中获取它,也不需要使用会话(因为如果你在网络农场中使用会话,你将不得不在数据库中的某个地方保存这个会话,所以无论如何,您将往返于数据库):

    [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;
    }
    

    【讨论】:

    • 所以这意味着,整个cookie保存在客户端(客户端不只发送session id然后匹配哪个服务器,而是客户端发送所有信息)?我在某处读到 cookie 客户端大小有 4k 限制,但这当然很多。您通常只需要用户名、id、文化、IP 等信息......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多