Asp.net MVC让用户在角色之间切换

时间:2011-02-17 14:29:37

标签: asp.net-mvc forms-authentication global-asax asp.net-roles

我正在开发一个复杂的网站,用户有多个角色。用户还会加入数据库中的其他项目,这些项目与他们的角色一起将定义他们在网站上可以看到和执行的操作。

现在,一些用户拥有多个角色,但由于结构复杂,网站一次只能处理1个角色。

这个想法是用户登录并在网站的一角有一个下拉列表,在那里他可以选择他的一个角色。如果他只有一个角色就没有下拉。

现在,我将最后选择的角色值存储在数据库中,用户可以使用其他设置。当他回来时,这种角色仍然被记住。

整个网站都应该可以访问下拉列表的值。 我想做两件事:

  1. 将当前角色存储在Session中。
  2. 覆盖IsInRole方法或编写IsCurrentlyInRole方法以检查对当前所选角色的所有访问权限,而不是所有角色,原始IsInRole方法
  3. 对于会话中的存储部分,我认为在Global.asax

    中执行此操作会很好
        protected void Application_AuthenticateRequest(Object sender, EventArgs e) {
            if (User != null && User.Identity.IsAuthenticated) {
                //check for roles session.
                if (Session["CurrentRole"] == null) {
                    NASDataContext _db = new NASDataContext();
                    var userparams = _db.aspnet_Users.First(q => q.LoweredUserName == User.Identity.Name).UserParam;
                    if (userparams.US_HuidigeRol.HasValue) {
                        var role = userparams.aspnet_Role;
                        if (User.IsInRole(role.LoweredRoleName)) {
                            //safe
                            Session["CurrentRole"] = role.LoweredRoleName;
                        } else {
                            userparams.US_HuidigeRol = null;
                            _db.SubmitChanges();
                        }
                    } else {
                        //no value
                        //check amount of roles
                        string[] roles = Roles.GetRolesForUser(userparams.aspnet_User.UserName);
                        if (roles.Length > 0) {
                            var role = _db.aspnet_Roles.First(q => q.LoweredRoleName == roles[0].ToLower());
                            userparams.US_HuidigeRol = role.RoleId;
                            Session["CurrentRole"] = role.LoweredRoleName;
                        }
                    }
                }
    
            }
        }
    

    但显然这会产生运行时错误。 Session state is not available in this context.

    1. 我该如何解决这个问题,就是这个问题 真的是最好的地方 码?
    2. 如何使用IPrincipal扩展用户(IsCurrentlyInRole?)而不会丢失所有其他功能
    3. 也许我这样做是错的,还有更好的办法吗?
    4. 非常感谢任何帮助。

2 个答案:

答案 0 :(得分:3)

是的,您无法在Application_AuthenticateRequest中访问会话 我创建了自己的CustomPrincipal。我将向您展示我最近所做的事情:

public class CustomPrincipal: IPrincipal
{
    public CustomPrincipal(IIdentity identity, string[] roles, string ActiveRole)
    {
        this.Identity = identity;
        this.Roles = roles;
        this.Code = code;
    }

    public IIdentity Identity
    {
        get;
        private set;
    }

    public string ActiveRole
    {
        get;
        private set;
    }

    public string[] Roles
    {
        get;
        private set;
    }

    public string ExtendedName { get; set; }

    // you can add your IsCurrentlyInRole 

    public bool IsInRole(string role)
    {
        return (Array.BinarySearch(this.Roles, role) >= 0 ? true : false);  
    }
}

如果有验证票据(用户已登录),我的Application_AuthenticateRequest会读取cookie:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[My.Application.FORMS_COOKIE_NAME];
    if ((authCookie != null) && (authCookie.Value != null))
    {
        Context.User = Cookie.GetPrincipal(authCookie);
    }
}


public class Cookie
    {
    public static IPrincipal GetPrincipal(HttpCookie authCookie)
    {
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        if (authTicket != null)
        {
            string ActiveRole = "";
            string[] Roles = { "" };
            if ((authTicket.UserData != null) && (!String.IsNullOrEmpty(authTicket.UserData)))
            {
            // you have to parse the string and get the ActiveRole and Roles.
            ActiveRole = authTicket.UserData.ToString();
            Roles = authTicket.UserData.ToString();
            }
            var identity = new GenericIdentity(authTicket.Name, "FormAuthentication");
            var principal = new CustomPrincipal(identity, Roles, ActiveRole );
            principal.ExtendedName = ExtendedName;
            return (principal);
        }
        return (null);
    }
 }

我已经扩展了我的cookie,添加了身份验证故障单的UserData。我在这里添加了额外的信息:

这是在登录后创建cookie的功能:

    public static bool Create(string Username, bool Persistent, HttpContext currentContext, string ActiveRole , string[] Groups)
    {
        string userData = "";

        // You can store your infos
        userData = ActiveRole + "#" string.Join("|", Groups);

        FormsAuthenticationTicket authTicket =
            new FormsAuthenticationTicket(
            1,                                                                // version
            Username,
            DateTime.Now,                                                     // creation
            DateTime.Now.AddMinutes(My.Application.COOKIE_PERSISTENCE),       // Expiration 
            Persistent,                                                       // Persistent
            userData);                                                        // Additional informations

        string encryptedTicket = System.Web.Security.FormsAuthentication.Encrypt(authTicket);

        HttpCookie authCookie = new HttpCookie(My.Application.FORMS_COOKIE_NAME, encryptedTicket);

        if (Persistent)
        {
            authCookie.Expires = authTicket.Expiration;
            authCookie.Path = FormsAuthentication.FormsCookiePath;
        }

        currentContext.Response.Cookies.Add(authCookie);

        return (true);
    }

现在您可以在应用中的任何位置访问您的信息:

CustomPrincipal currentPrincipal = (CustomPrincipal)HttpContext.User;

因此您可以访问自定义主体成员:currentPrincipal.ActiveRole

当用户更改角色(活动角色)时,您可以重写cookie。

我忘了说我在authTicket.UserData中存储了一个JSON序列化的类,因此很容易反序列化和解析。

您可以找到更多信息here

答案 1 :(得分:0)

如果您确实希望用户一次只有一个活动角色(如想要覆盖IsInRole所暗示的那样),那么将所有用户的“潜在”角色存储在一个单独的位置可能是最容易的,但仅限于实际上允许它们一次处于1个ASP.NET身份验证角色。当他们选择一个新角色时,使用内置方法将它们从当前角色中删除,并将它们添加到新角色中。