我将如何模仿User.IsInRole()

Don*_*yle 6 asp.net-mvc entity-framework simplemembership

我有一个使用VS 2012 Internet Application(简单会员)EF Code First构建的网站

更新

我想知道如何扩展HttpContext.User.IsInRole(role)自定义表的功能 - > User.IsInClient(client).

jwa*_*zko 15

以下是我建议您解决问题的方法:

创建您自己的实现接口System.Security.Principal,您可以在其中放置您需要的任何方法:

public interface ICustomPrincipal : IPrincipal
{
    bool IsInClient(string client);
}
Run Code Online (Sandbox Code Playgroud)

实现此接口:

public class CustomPrincipal : ICustomPrincipal
{
    private readonly IPrincipal _principal;

    public CustomPrincipal(IPrincipal principal) { _principal = principal; }

    public IIdentity Identity { get { return _principal.Identity; } }
    public bool IsInRole(string role) { return _principal.IsInRole(role); }

    public bool IsInClient(string client)
    {
        return _principal.Identity.IsAuthenticated 
               && GetClientsForUser(_principal.Identity.Name).Contains(client);
    }

    private IEnumerable<string> GetClientsForUser(string username)
    {
        using (var db = new YourContext())
        {
            var user = db.Users.SingleOrDefault(x => x.Name == username);
            return user != null 
                        ? user.Clients.Select(x => x.Name).ToArray() 
                        : new string[0];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Global.asax.cs中,将自定义主体分配给请求用户上下文(如果您打算稍后使用,则可选择将其分配给正在执行的主题).我建议使用Application_PostAuthenticateRequest不用Application_AuthenticateRequest于此赋值的事件,否则将覆盖您的主体(至少通过ASP.NET MVC 4):

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
    Context.User = Thread.CurrentPrincipal = new CustomPrincipal(User);

    /* 
     * BTW: Here you could deserialize information you've stored earlier in the 
     * cookie of authenticated user. It would be helpful if you'd like to avoid 
     * redundant database queries, for some user-constant information, like roles 
     * or (in your case) user related clients. Just sample code:
     *  
     * var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
     * var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
     * var cookieData = serializer.Deserialize<CookieData>(authCookie.UserData);
     *
     * Next, pass some deserialized data to your principal:
     *
     * Context.User = new CustomPrincipal(User, cookieData.clients);
     *  
     * Obviously such data have to be available in the cookie. It should be stored
     * there after you've successfully authenticated, e.g. in your logon action:
     *
     * if (Membership.ValidateUser(user, password))
     * {
     *     var cookieData = new CookieData{...};         
     *     var userData = serializer.Serialize(cookieData);
     *
     *     var authTicket = new FormsAuthenticationTicket(
     *         1,
     *         email,
     *         DateTime.Now,
     *         DateTime.Now.AddMinutes(15),
     *         false,
     *         userData);
     *
     *     var authTicket = FormsAuthentication.Encrypt(authTicket);
     *     var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, 
                                           authTicket);
     *     Response.Cookies.Add(authCookie);
     *     return RedirectToAction("Index", "Home");
     * }
     */         
}
Run Code Online (Sandbox Code Playgroud)

接下来,为了能够使用属性UserHttpContext控制器没有它铸造于ICustomPrincipal每一次,定义基本控制器,其中覆盖了默认的User属性:

public class BaseController : Controller
{
    protected virtual new ICustomPrincipal User
    {
        get { return (ICustomPrincipal)base.User; }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,让其他控制器继承它:

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        var x = User.IsInClient(name); 
Run Code Online (Sandbox Code Playgroud)

如果您使用Razor View Engine,并且希望能够以非常相似的方式在视图上使用您的方法:

@User.IsInClient(name)
Run Code Online (Sandbox Code Playgroud)

你需要重新定义WebViewPage类型:

public abstract class BaseViewPage : WebViewPage
{
    public virtual new ICustomPrincipal User
    {
        get { return (ICustomPrincipal)base.User; }
    }
}

public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
    public virtual new ICustomPrincipal User
    {
        get { return (ICustomPrincipal)base.User; }
    }
}
Run Code Online (Sandbox Code Playgroud)

并通过修改Views\Web.config文件的相应部分告诉Razor反映您的更改:

<system.web.webPages.razor>
    ...
    <pages pageBaseType="YourNamespace.BaseViewPage">
Run Code Online (Sandbox Code Playgroud)

  • @Don Thomas Boyle:`WebViewPage`是来自`System.Web.Mvc`命名空间的合法框架类. (2认同)
  • @Don Thomas Boyle:"不可见"是什么意思?`WebViewPage`类型表示呈现使用ASP.NET Razor语法的视图所需的属性和方法(http://msdn.microsoft.com/en-us/library/gg402107(v=vs.108)的.aspx).您可以通过覆盖此类并在Web.config中指向Razor来使用视图上的自定义类型`User`对象来使用它,而不是默认值. (2认同)
  • @Don Thomas Boyle:N/P. 你只需要通过这样做以及简单的使用来获得智能支持,因为Razor会知道你的类型.所以不是`@(((ICustomPrincipal)User).IsInClient(...))`,就足够了:`@ User.IsInClient(...)`. (2认同)
  • @Don Thomas Boyle:如果您想在自定义的"IPrincipal"实现中避免冗余数据库查询,我已经添加了如何在cookie中存储/分配经过身份验证的用户数据的信息. (2认同)