Asp.net mvc 4 将角色与表单身份验证一起使用

Asp.net mvc 4 将角色与表单身份验证一起使用,asp.net-mvc-4,authorization,forms-authentication,Asp.net Mvc 4,Authorization,Forms Authentication,我在MVC应用程序中使用表单身份验证。这很好用。但我不想调整授权,只允许某些角色的人。登录名对应于active directory中的用户,角色对应于用户所在的组 对于身份验证,我只需在验证登录后调用FormsAuthentication.SetAuthCookie(用户名,true) 为了进行授权,我首先将该属性应用于要保护的控制器 [Authorize(Roles = "AllowedUsers")] public class MyController ... 接下来,我将在global.

我在MVC应用程序中使用表单身份验证。这很好用。但我不想调整授权,只允许某些角色的人。登录名对应于active directory中的用户,角色对应于用户所在的组

对于身份验证,我只需在验证登录后调用FormsAuthentication.SetAuthCookie(用户名,true)

为了进行授权,我首先将该属性应用于要保护的控制器

[Authorize(Roles = "AllowedUsers")]
public class MyController
...
接下来,我将在global.asax中处理OnAuthenticate事件

protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs args)
{
    if (FormsAuthentication.CookiesSupported)
        {
            if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
            {
                try
                {
                    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
                    Request.Cookies[FormsAuthentication.FormsCookieName].Value);

                    // Create WindowsPrincipal from username.  This adds active directory
                    // group memberships as roles to the user.
                    args.User = new WindowsPrincipal(new WindowsIdentity(ticket.Name));

                    FormsAuthentication.SetAuthCookie(ticket.Name, true);
                }
                catch (Exception e)
                {
                    // Decrypt method failed.
                }
        }
    }
    else
    {
        throw new HttpException("Cookieless Forms Authentication is not " + "supported for this application.");
    }
}
当有人访问网站时,他们会看到登录屏幕。他们实际上可以从那里登录。然而,不知何故,它并没有保存auth cookie,他们在单击下一个链接后会看到一个登录屏幕。我尝试在OnAuthenticate()中添加对SetAuthCookie()的调用,但没有效果

在我添加此事件处理程序来处理授权之前,身份验证工作正常。因此,框架中的某个地方正在设置用户。我想知道这是否是正确的方法,我只是错过了一些东西,或者我是否需要一个不同的方法

我需要做些什么才能让它工作

谢谢,
Scott

角色也可以存储在cookie中,您至少有两个选项:

  • 角色提供程序cookie(另一个支持表单cookie的cookie),在
    web.config
    中的角色提供程序配置上设置为
    cacheRolesInCookie=“true”
    。授权模块第一次请求角色时读取角色,然后发出cookie

  • 在表单cookie的userdata部分中存储角色的自定义角色提供程序,必须手动将角色添加到表单cookie的user data部分

授权
模块向当前主体询问用户角色,如果启用了角色提供程序,则会扫描角色cookie(第一个选项)或触发自定义角色提供程序方法

另一种推荐的方法是切换到会话身份验证模块(SAM),它可以替代表单身份验证。有一些重要的优点,包括SAM从cookie中重新创建
ClaimsPrincipal
,角色只是
Role
声明:

// create cookie

SessionAuthenticationModule sam = 
   (SessionAuthenticationModule)
   this.Context.ApplicationInstance.Modules["SessionAuthenticationModule"];

ClaimsPrincipal principal = 
   new ClaimsPrincipal( new GenericPrincipal( new GenericIdentity( "username" ), null ) );

// create any userdata you want. by creating custom types of claims you can have
// an arbitrary number of your own types of custom data
principal.Identities[0].Claims.Add( new Claim( ClaimTypes.Role, "role1" ) );
principal.Identities[0].Claims.Add( new Claim( ClaimTypes.Role, "role2" ) );

var token = 
    sam.CreateSessionSecurityToken( 
        principal, null, DateTime.Now, DateTime.Now.AddMinutes( 20 ), false );
sam.WriteSessionTokenToCookie( token );
从现在起,身份将存储在cookie中并自动管理,是的,控制器上的
Authorization
属性将按预期工作

在我的博客上阅读有关用SAM替换表单模块的更多信息:


看来我最初的方法行不通。我试图让ASP.NET自动从他们的广告帐户加载用户角色。没有就这是否可能发表评论。然而,我所做的研究表明,我必须编写代码将广告组成员身份加载到用户角色中

创建ASP.NET MVC使用的用户主体的解决方案似乎是在FormsAuthentication_OnAuthenticate()中创建它,并将其分配给Context.user。如果未设置上下文,则会出现此错误。用户ASP.NET MVC在FormsAuthentication_OnAuthenticate()返回后,根据身份验证票证创建用户主体。此外,如果我在FormsAuthentication_onAuthentication()中设置了Context.User,则ASP.NET MVC似乎与Context.User无关

下面是我最后做的事情

这是处理身份验证的代码

public ActionResult LogOn(FormCollection collection, string returnUrl)
{
    // Code that authenticates user against active directory
    if (authenticated)
    {
        var authTicket = new FormsAuthenticationTicket(username, true, 20);

        string encryptedTicket = FormsAuthentication.Encrypt(authTicket);

        var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
        authCookie.Expires = DateTime.Now.AddMinutes(30);
        Response.Cookies.Add(authCookie);

        if (Url.IsLocalUrl(returnUrl)
            && returnUrl.Length > 1
            && returnUrl.StartsWith("/", StringComparison.OrdinalIgnoreCase)
            && !returnUrl.StartsWith("//", StringComparison.OrdinalIgnoreCase)
            && !returnUrl.StartsWith("/\\", StringComparison.OrdinalIgnoreCase))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return Redirect("~/");
        }
   }
   return View();
}
我最初尝试调用FormsAuthentication.SetAuthCookie(用户名,true),而不是手动创建、加密并将其添加到响应cookie集合中。这在开发环境中起了作用。然而,在我把它发布到网站上之后,它就没有了

这是注销代码

public ActionResult LogOff()
{
    var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        authCookie.Expires = DateTime.Today.AddDays(-1);
    }

    Response.Cookies.Add(authCookie);
    FormsAuthentication.SignOut();

    return RedirectToAction("Index", "Home");
}
在我切换到手动创建、加密并将身份验证票证添加到登录代码中的响应cookie集合之后,FormsAuthentication.SignOut()似乎没有做任何事情。所以我不得不手动使cookie过期

这是我用于FormsAuthentication_OnAuthenticate()的代码

UserData是我创建的一个类,用于处理缓存用户角色。这是必需的,因为active directory返回用户所属的组成员身份需要时间。为了完整起见,下面是我为UserData编写的代码

public class UserData
{
    private int _TimeoutInMinutes;
    private string[] _Roles = null;

    public string UserName { get; private set; }
    public DateTime Expires { get; private set; }
    public bool Expired { get { return Expires < DateTime.Now; } }
    public string[] Roles
    {
        get
        {
            if (Expired || _Roles == null)
            {
                _Roles = GetADContainingGroups(UserName).ToArray();
                Expires = DateTime.Now.AddMinutes(_TimeoutInMinutes);
            }
            return _Roles;
        }
    }

    public UserData(string userName, int timeoutInMinutes = 20)
    {
        UserName = userName;
        _TimeoutInMinutes = timeoutInMinutes;
    }
}
公共类用户数据
{
私用整数分钟;
私有字符串[]_Roles=null;
公共字符串用户名{get;private set;}
公共日期时间过期{get;private set;}
public bool Expired{get{return Expires
您是否尝试转储当前用户所在的组/声明列表?我怀疑他们在那里,但作为广告组SID不是组名。很抱歉延迟响应。工作中出现了一些问题。总之,这是一个有趣的理想。但是,我不喜欢在cookie中缓存用户角色的想法。难道用户不可能修改cookies的内容并给自己任何他们想要的角色吗?ASP.NET身份验证Cookie是加密的,或者可能只是以64进制编码。所以这并不容易。我仍然认为我会将其缓存在应用程序变量或数据库中。这也将使POST/GET中发送的数据量最小化。身份验证cookie始终是加密的。
public class UserData
{
    private int _TimeoutInMinutes;
    private string[] _Roles = null;

    public string UserName { get; private set; }
    public DateTime Expires { get; private set; }
    public bool Expired { get { return Expires < DateTime.Now; } }
    public string[] Roles
    {
        get
        {
            if (Expired || _Roles == null)
            {
                _Roles = GetADContainingGroups(UserName).ToArray();
                Expires = DateTime.Now.AddMinutes(_TimeoutInMinutes);
            }
            return _Roles;
        }
    }

    public UserData(string userName, int timeoutInMinutes = 20)
    {
        UserName = userName;
        _TimeoutInMinutes = timeoutInMinutes;
    }
}