6

我正在尝试使用 MVC 4 中的表单身份验证对用户进行身份验证(我正在使用 RavenDB,因此我无法使用标准成员资格提供程序)。然后稍后我使用该User.IsInRole()方法或AuthorizeAttribute验证用户是否处于人员角色。

这是我设置成功身份验证票的地方(目前UserController.cs):

FormsAuthenticationTicket ticket =
    new FormsAuthenticationTicket(
        1,
        model.Email,
        DateTime.Now,
        DateTime.Now.AddDays(1),
        false,
        model.Email);

string hashedTicket = FormsAuthentication.Encrypt(ticket);

HttpCookie cookie =
    new HttpCookie(
        FormsAuthentication.FormsCookieName,
        hashedTicket);

HttpContext.Response.Cookies.Add(cookie);

这是我检查每个请求的票证的地方 ( Global.asax):

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];

    if (authCookie != null)
    {
        var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        var user = this.UserService.GetUserByEmail(authTicket.Name);

        var identity = new GenericIdentity(authTicket.Name, "Forms");

        var principal = new GenericPrincipal(identity, user.Roles);

        HttpContext.Current.User = principal;
    }
}

如果我然后在我的一种操作方法(CalendarController.cs)上放置一个调试点,我会得到isStaffequals false

public ActionResult Index()
{
    var user = HttpContext.User;

    bool isStaff = user.IsInRole(Role.Staff);

    return View();
}

只是为了完成(Roles.cs,只是一个用于测试事物的临时类):

public static class Role
{
    public static string Staff
    {
        get { return "Staff"; }
    }

    public static string Manager
    {
        get { return "Manager"; }
    }
}

任何人都可以帮我说明我可能会错过什么吗?当我到达操作方法时,我设置的角色似乎正在消失。

4

3 回答 3

13

谢谢大家帮我解决这个问题,我想出的(包括在下面)效果很好!如果用户拥有有效票证 (cookie),它会自动通过登录屏幕直接登录用户,并且还使用ClaimsIdentityClaimsPrincipal对象处理基于声明的角色,而不将角色放在用户的 cookie 中。它还可以处理Global.asax.cs文件中的身份验证,而无需使用自定义授权属性。

用户控制器.cs

public ActionResult Login()
{
    LoginViewModel model = new LoginViewModel();

    if ((HttpContext.User != null) &&
        (HttpContext.User.Identity.IsAuthenticated))
    {
        return RedirectToAction("Index", "Home");
    }

    return View(model);
}

[HttpPost]
public ActionResult Login(LoginViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    bool isAuthenticated = this.userService.IsPasswordValid(model.Email, model.Password);

    if (!isAuthenticated)
    {
        ModelState.AddModelError("AuthError", Resources.User.Login.AuthError);

        return View(model);
    }

    FormsAuthentication.SetAuthCookie(model.Email, model.RememberUser);

    return RedirectToAction("Index", "Home");
}

全球.asax.cs

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];

    if (authCookie != null)
    {
        var ticket = FormsAuthentication.Decrypt(authCookie.Value);

        FormsIdentity formsIdentity = new FormsIdentity(ticket);

        ClaimsIdentity claimsIdentity = new ClaimsIdentity(formsIdentity);

        var user = this.UserService.GetUserByEmail(ticket.Name);

        foreach (var role in user.Roles)
        {
            claimsIdentity.AddClaim(
                new Claim(ClaimTypes.Role, role));
        }

        ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

        HttpContext.Current.User = claimsPrincipal;
    }
}
于 2013-01-30T22:37:16.827 回答
3

由于您使用的是 Raven,我假设您制作了自己的自定义 MembershipProvider 和 RoleProvider;并修改 web.config 以使用它们。你应该有一个类似这样的条目:

<membership defaultProvider="MyMembershipProvider">
  <providers>
    <add name="MyMembershipProvider" type="namespace.MyMembershipProvider, providerAssemblyName" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
  </providers>
</membership>
<roleManager enabled="true" defaultProvider="DefaultRoleProvider">
  <providers>
    <add connectionStringName="DefaultConnection" applicationName="/" name="DefaultRoleProvider" type="namespace.MyRoleProvider, providerAssemblyName" />
  </providers>
</roleManager>

如果您使用 .NET Framework 4.5 版,它使用基于声明的安全性,您不需要将角色存储在 cookie 中。相反,角色只是存储在ClaimsPrincipal中的另一个声明。所有主体现在都从 ClaimsPrincipal 继承,并存储在用户会话中

System.Web.HttpContext.Current.User as ClaimsPrincipal

如果您的成员资格和角色提供程序设置正确,ASP.NET 应该使用它们来填充 ClaimsPrincipal 中的角色,然后在您检查IsInRole时检查声明。

您还可以从 ClaimsPrincipal 中检索此类角色的角色。

principal.FindAll(ClaimTypes.Role).Select(p => p.Value);

您可以像这样向 ClaimsPrincipal 添加角色。

List<Claim> claims = new List<Claim>();
foreach (string role in roles)
   claims.Add(new Claim(ClaimTypes.Role, role));
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Forms"));

现在你可以像这样在认证后设置你的cookie。

FormsAuthentication.SetAuthCookie(username, false);
于 2013-01-30T15:44:22.977 回答
2

您没有创建 FormsAuthenticationTicket 插入角色信息:

        var ticket = new FormsAuthenticationTicket(
                1, //ticket version
                userName,
                DateTime.Now,
                DateTime.Now.Add(timeout), //timeout
                true, //persistent cookies
                roles,// <---ROLES not model.Email
                FormsAuthentication.FormsCookiePath);

- - - 编辑 - - -

忘记我说的话:我认为您调用 IsInRole() 太早或 user.Roles 的值错误(可能是字符串中的空格:isinrole 使用 StringComparison.OrdinalIgnoreCase),或者您应该使用 FormsIdentity 而不是 GenericIdentity。

调试器说什么?

供参考: http: //pastebin.com/jkqqcg28 (这是我用于处理身份验证的起始模型)

于 2013-01-30T14:16:42.540 回答