【问题标题】:forms authentication in mvc app, authenticating with external servicemvc 应用程序中的表单身份验证,使用外部服务进行身份验证
【发布时间】:2012-10-21 18:23:28
【问题描述】:

我目前正在开发一个 MVC4 应用程序,在该应用程序中,不同类型的用户可以访问该站点并登录。将有 2 种不同的用户类别,A 类和 B 类,可以访问该站点的不同区域。

用户的身份验证是通过对身份验证提供程序的服务调用,他们将返回用户是否已通过身份验证的状态以及用户信息,例如姓名、地址等。此时我想跟踪用户已通过身份验证以及他们的角色。以前我会创建一个包含用户名、地址等的 cookie 并存储在客户端计算机上。但这很容易被操纵,因为 cookie 是纯文本。

但我想加强网站的安全性,因此考虑使用 FormsAuthentication。我知道这会生成一个 cookie,但它更安全。

这是正确的方法吗?可以将地址等用户信息存储在此 cookie 中吗?我想存储它的会话对我不可用。我将如何使用 FormsAuthentication 实现这一点?

我的第二个问题是,一旦用户登录,我如何知道他们的角色?

最后,一旦用户登录,整个网站是否应该通过https?即使是对所有人开放但由登录用户访问的区域?登录的用户将在每个页面的屏幕上显示他们的信息,例如 Welcome John。

【问题讨论】:

    标签: c# asp.net-mvc security asp.net-mvc-4 forms-authentication


    【解决方案1】:

    这是正确的方法吗?用户的地址等信息可以存储在这个cookie中吗?

    您是正确的,Forms Authentication 默认情况下会将用户详细信息存储在加密的 cookie 中,因此它比您现在拥有的更安全。

    使用表单身份验证,您可以指定进入客户端 cookie 的字符串内容。但是,我认为您应该存储在 cookie 中的唯一信息是用户标识符和角色标识符。这些信息应该是根据请求对用户进行身份验证和授权所必需的全部信息。任何额外的信息(例如地址)只会增加 cookie 的权重,并且可以在需要时从服务中获取。

    要使用表单身份验证,只需将身份验证元素添加到 web.config 的 system.web 部分:

    <authentication mode="Forms">
      <forms loginUrl="~/Login/Home" timeout="60" />
    </authentication>
    

    调用服务验证用户后,通过 FormsAuthentication 静态类设置 Cookie 数据。 Cookie 数据需要包含用户的标识符(例如,如果您想显示用户的姓名)和角色标识符(解释如下)。

    var username = // get name from service
    var role = // get role from service
    FormsAuthentication.SetAuthCookie(username + "-" + role, keepUserLoggedIn);
    

    当用户退出时使用同一个类来移除cookie。

    FormsAuthentication.SignOut();
    

    我的第二个问题是,一旦用户登录,我如何知道他们的角色?

    asp.net MVC 提供了一种简单的机制,只允许特定用户使用应用程序的某些区域。通过在任何控制器类或操作方法上使用 [Authorise] 属性,仅允许登录用户访问 - 即存在 FormsAuthentication cookie 的用户。未登录的用户将被重定向到 web.config 的 authenication 元素中指定的 LoginUrl。

    Authorize 属性也有一个重载,它只允许特定角色的用户访问控制器/方法。这将只允许具有“管理员”角色的登录用户访问控制器的任何操作方法。

    [Authroise("Admin")]
    public class EditController : Controller
    {}
    

    然后您需要创建自己的系统接口RoleProvider 的实现。该接口包含许多方法,但并非所有方法都是必需的。您需要实施以检查用户所在角色的唯一方法是GetRolesForUser[Authorise] 属性将使用它来确定用户是否可以查看。

    public class MyRoleProvider : RoleProvider
    {
        public override string[] GetRolesForUser(string username)
        {
            var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    
            if (authCookie != null && !String.IsNullOrEmpty(authCookie.Value))
            {
                var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                var roles = authTicket.UserData.Split(',')[1];
                return roles.Split(';');
            }
    
            throw new MemberAccessException("User not logged in");
        }
    }
    

    然后,您只需告诉您的应用程序在 Authorise] 属性启动时使用您的 RoleProvider 实现。这也可以通过 system.web 元素中的 web.config 完成。

    <roleManager defaultProvider="MyRoleProvider" enabled="true">
      <providers>
        <clear />
        <add name="MyRoleProvider" type="My.Namespace.MyRoleProvider" />
      </providers>
    </roleManager>
    

    最后,一旦用户登录,整个网站是否应该通过https?即使是对所有人开放但已登录用户访问的区域?

    理想情况下,是的。一旦用户登录并且您设置了 FormsAuthentication cookie,您需要保护该 cookie 免受外部拦截。为了防止这种情况,您应该在应用程序的所有区域强制执行 https 协议。如果用户的请求被拦截并制作了 cookie 的副本,那么“中间人”可以在他的浏览器中设置 cookie 的副本并获得对您网站受保护区域的访问权限。使用 MVC,您可以在任何特定的控制器或操作方法上使用 [RequireHttpsAttribute],类似于 [Authorise] 属性。在 MVC4 中,有一种简单的方法可以“全局”应用它。在 Web 项目的 App_Start 文件夹中,找到 FilterConfig 类并将以下行包含到 RegisterGlobalFilters 方法中:

    filters.Add(new RequireHttpsAttribute());
    

    【讨论】:

    • 非常感谢您提供如此出色的答案,涵盖了我的所有观点。根据您给出的最后一个答案,只需提出 2 个问题 - 1. 如果我的网站仅在用户登录而不是正常登录时才需要是 https,这是如何实现的? 2. 加密 cookie 的内容并仅通过 https 提供它们是否过度?
    • 1.也许我不清楚:如果他们有任何登录功能,您的网站应该完全是 https。这是为了保护您的用户通过登录表单提交的凭据,以便在他们通过网站时确保加密的 cookie 安全。 2. 并不过分。考虑一个登录用户暂时离开他们的机器,一个精通浏览器的人可以检查 cookie 并简单地读取用户名和密码。
    • 并不是说您应该将加密的密码保存在 cookie 中,但您明白了。
    • 嗯,好的-感谢您的澄清。我将研究使整个网站成为 https 的可能性。如果这不可行,是否会在没有 https 的情况下加密 cookie,但有足够的安全步骤?顺便说一句,我担心加密所有 cookie 的性能开销,所以我的另一个选择是 https 站点(如果可行)而不加密 cookie?
    • 我会优先考虑 cookie 加密而不是实施 https。尽管如果您购买了有效的 ssl 证书,则没有理由不能两者兼得。如果您拥有证书,可以说在整个站点上强制执行 https 比仅在某些区域强制执行它更容易(请参阅我在答案中关于如何全局应用 RequireHttps 属性的观点)。当然也不用担心加密/解密的开销。表单身份验证在优化这一步方面做得很好,考虑到它的重要性,我认为开销完全可以忽略不计
    猜你喜欢
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 2017-03-03
    • 2012-09-06
    • 2011-06-27
    • 2013-11-22
    • 2019-01-28
    • 2023-03-22
    相关资源
    最近更新 更多