2

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

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

这个想法是用户登录并在网站的角落有一个下拉菜单,他可以在其中选择他的角色之一。如果他只有 1 个角色,则没有下拉菜单。

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

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

  1. 将当前角色存储在Session.
  2. 重写该IsInRole方法或编写一个IsCurrentlyInRole方法来检查对当前选定角色的所有访问权限,而不是所有角色,就像原始IsInRole方法一样

对于会话部分的存储,我认为这样做会很好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 回答 2

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,添加了 Authentication Ticket 的 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 序列化的类,因此很容易反序列化和解析。

你可以在这里找到更多信息

于 2011-02-18T13:02:32.983 回答
0

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

于 2011-02-17T15:12:23.503 回答