umbraco public 认证时访问错误

umbraco public access error when authenticated

我在 Umbraco 7 中遇到 public 访问问题。

我使用自定义会员提供程序通过我的 CRM 数据库对用户进行身份验证。 我设置了一个规则,只允许 经过身份验证的(前端)用户访问 并且我使用自定义角色提供程序来定义经过身份验证的用户具有访问者角色。如果他们未通过身份验证,他们将被重定向到登录页面。

当我调试网站时,用户的角色是:

我已通过身份验证,当前用户的角色是正确的。

但我仍然被重定向到登录页面!没看懂。

我的角色提供者:

public class CustomRoleProvider : Umbraco.Web.Security.Providers.MembersRoleProvider
{
    const int SITE_ID = 6;

    public override string ApplicationName
    {
        get
        {
            return "Site";
        }
    }

    public override string[] GetAllRoles()
    {
        return new[] { Const.VISITORS_LABEL };
    }

    public override string[] GetRolesForUser(string username)
    {
        return new[] { Const.VISITORS_LABEL };
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="username"></param>
    /// <param name="roleName"></param>
    /// <returns></returns>
    public override bool IsUserInRole(string username, string roleName)
    {
        //every user is a visitor
        if(roleName == Const.VISITORS_LABEL)
        {
            return true;
        }

        else
        {
            return base.IsUserInRole(username, roleName);
        }
    }

    public override string[] GetUsersInRole(string roleName)
    {
        if(roleName == Const.VISITORS_LABEL)
        {
            using (var db = new CRMEntities())
            {
                var usersEmails = db.Customer_View.Where(x => x.SiteID == SITE_ID).Select(x=>x.Email).ToArray();
                return usersEmails;
            }
        }
        else
        {
            return base.GetUsersInRole(roleName);
        }
    }
}

这是我用于身份验证的控制器:

public class MemberLoginSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
    // The MemberLogin Action returns the view, which we will create later. It also instantiates a new, empty model for our view:

    [HttpGet]
    [ActionName("MemberLogin")]
    public ActionResult MemberLoginGet()
    {
        return PartialView("MemberLogin", new MemberLoginModel());
    }

    // The MemberLogout Action signs out the user and redirects to the site home page:

    [HttpGet]
    public ActionResult MemberLogout()
    {
        Session.Clear();
        FormsAuthentication.SignOut();
        return Redirect("/");
    }

    // The MemberLoginPost Action checks the entered credentials using the standard Asp Net membership provider and redirects the user to the same page. Either as logged in, or with a message set in the TempData dictionary:
    [HttpPost]
    [ActionName("MemberLogin")]
    public ActionResult MemberLoginPost(MemberLoginModel model)
    {
        if (Membership.ValidateUser(model.Username, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe);

            return RedirectToCurrentUmbracoPage();
        }

        else
        {
            TempData["Status"] = "Invalid username or password";
            return RedirectToCurrentUmbracoPage();
        }
    }
}

我的角色提供者在 web.config 中,并且 Visitors 角色在管理面板中被检测为角色。

<roleManager enabled="true" defaultProvider="CustomRoleProvider">
  <providers>
    <clear />
    <add name="UmbracoRoleProvider" type="Umbraco.Web.Security.Providers.MembersRoleProvider" />
    <add name="CustomRoleProvider" type="*.UI.Helpers.CustomRoleProvider" />
  </providers>
</roleManager>

编辑: 我忘记了会员提供商:

 public class MyMembershipProvider : Umbraco.Web.Security.Providers.MembersMembershipProvider
{

const int SITE_ID = 6;
    //we dont let user change their password using RC website
    public override bool AllowManuallyChangingPassword
    {
        get
        {
            return false;
        }
    }

    public override bool EnablePasswordReset
    {
        get
        {
            return false;
        }
    }

    public override bool EnablePasswordRetrieval
    {
        get
        {
            return false;
        }
    }

    public override bool ValidateUser(string username, string password)
    {
        Customer_View user;

        //just to avoid errors with uppercase letters
        username = username.ToLowerInvariant();

        using (var db = new CRMEntities())
        {
            user = db.Customer_View.SingleOrDefault(x => x.Email == username && x.SiteID == SITE_ID);

            //no user with this email
            if (user == null)
                return false;

            //check if password is same
            return user.Password == password;
        }
    }

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        //just to avoid errors with uppercase letters
        username = username.ToLowerInvariant();
        MembershipUser toReturn;

        using (var db = new CRMEntities())
        {
            Customer_View user = db.Customer_View.SingleOrDefault(x => x.Email == username && x.SiteID == SITE_ID);

            toReturn = user != null ? new MembershipUser(
                //provider name
                "MyMembershipProvider", string.Format("{0} {1}", user.FirstName, user.LastName),
                username, username, string.Empty, string.Empty, true, true, user.CreateDate, new DateTime(), new DateTime(), new DateTime(), new DateTime()) :

                null;

        }

        return toReturn;
    }

    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
    {
        return this.GetUser(providerUserKey as string, userIsOnline);
    }


}

每次我尝试访问具有特定访问权限的页面时,即使我已通过身份验证,我也会被重定向到登录页面:

我该如何解决?

使用 Umbraco 的 public 访问权限,当用户注销并尝试访问受保护的页面时,他们将看到登录页面。但是,他们地址栏中的 URL 将是他们尝试访问的页面。

验证用户后 RedirectToCurrentUmbracoPage() 将实际执行完全重定向到登录页面,地址栏中的 URL 将相应更新。您真正想要做的是将它们重定向到当前 URL。您可以通过将 MemberLoginPost 方法中的第一个 RedirectToCurrentUmbracoPage() 替换为 RedirectToCurrentUmbracoUrl().

来完成此操作

您还使用 RedirectToCurrentUmbracoPage() 来处理用户凭据不正确的情况,这也会导致完全重定向到登录页面。如果您只是向用户 return CurrentUmbracoPage() 然后一切都应该正常工作。请参阅下面的更新方法:

[HttpPost]
[ActionName("MemberLogin")]
public ActionResult MemberLoginPost(MemberLoginModel model)
{
    if (Membership.ValidateUser(model.Username, model.Password))
    {
        FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe);

        return RedirectToCurrentUmbracoUrl();
    }

    else
    {
        TempData["Status"] = "Invalid username or password";

        return CurrentUmbracoPage();
    }
}

为什么要添加自定义角色提供者。保持简单。如果您有网站的注册页面,您可以通过编程方式分配成员类型和成员角色

如果您从后端添加成员,您可以轻松添加 "Visitor" 角色。

因此在这两种情况下,"Visitor" 角色都可以轻松应用于所有成员,并且您可以轻松地将您的页面保留在访问者角色登录后(所有身份验证)。

编辑: 我已经删除了以编程方式向用户添加角色的代码,因为您不需要它,解决方案如下:

如您所知,自定义角色提供程序和自定义成员资格提供程序齐头并进。您已经添加了自定义成员提供程序并覆盖了 ValidateUser 方法,但是要使所有这些工作正常,您需要覆盖 GetUser 的另外两种方法 请参见下面的自定义提供程序代码,它会起作用

MemberShipProvider

public class MyMembershipProvider : MembersMembershipProvider
{
    public override bool ValidateUser(string username, string password)
    {
        if (base.ValidateUser(username,password))
        {
            //if this is umbraco user validate by base method
            return true;
        }
        else
        {
            var allow = //add your validation code for CRM, I have checked if username is "tester" and allowed for testing purpose.
            return allow;
        }
    }
    // These two methods below which you have not overridden and need to override for public access to work

    public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
    {
        if(base.GetUser(providerUserKey, false)!=null)
            //if this is umbraco user add it as is.
            return base.GetUser(providerUserKey,userIsOnline);
        else
            //Add your CRM user, I do not have database, so added test user
            return new MembershipUser("UmbracoMembershipProvider", "tester", 1233, "tester@test.com", null, null, true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
    }
    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        if (true)//check if this is CRM user here
        {
            return new MembershipUser("UmbracoMembershipProvider", "tester", 1233, "tester@test.com", null, null, true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
            //I am adding test user, you should create user from your CRM database
        }
        else
            return base.GetUser(username,false);

    }

}

EDIT2

好的,我已经调试了 roleprovider 和 membershipprovider 中的每一位代码,当用户登录后,当用户尝试访问受保护的页面时,首先调用 GetUser(string username, bool userIsOnline),如果失败,returns 登录页面,成功后 GetUser(object providerUserKey, bool userIsOnline) 接到电话。如果此 returns 为空,则显示访问权限不足的页面,否则将调用 GetRolesForUser(string username)。并显示所有成功页面。我在两个文件的每个方法上都设置了断点,所以只涉及这3个方法。正如您看到的登录页面,我的猜测是 GetUser(string username, bool userIsOnline) 第一次调用在某处失败了。

如果有帮助,下面是我的文件

角色配置部分

<roleManager enabled="true" defaultProvider="UmbracoRoleProvider">
  <providers>
    <clear />
    <!--<add name="UmbracoRoleProvider" type="Umbraco.Web.Security.Providers.MembersRoleProvider" />-->
    <add name="UmbracoRoleProvider" type="Assembly.Providers.MyRolesProvider" />
  </providers>
</roleManager>

会员配置部分

<membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="UmbracoMembershipProvider" type="Assembly.Providers.MyMemberShipProvider, Assembly" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" />
    <!--<add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />-->
    <add name="UsersMembershipProvider" type="Assembly.Providers.MyUserMembershipProvider, Assembly" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
  </providers>
</membership>

账户控制器

public class AccountSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
    [HttpPost]
    public ActionResult LoginForm(LoginModel model)
    {
        //model not valid, do not save, but return current umbraco page
        if (!ModelState.IsValid)
        {
            //Perhaps you might want to add a custom message to the TempData or ViewBag
            //which will be available on the View when it renders (since we're not 
            //redirecting)          
            return CurrentUmbracoPage();
        }

        // Login
        if (Membership.ValidateUser(model.Username, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.Username, false);
            return RedirectToCurrentUmbracoUrl();
        }
        else
        {
            ModelState.AddModelError("Username", "Username is not valid");
            return CurrentUmbracoPage();
        }
    }
}

public class LoginModel
{
    [Required]
    public string Username { get; set; }

    [Required]
    [DataType(DataType.Password)] 
    public string Password { get; set; }
}

角色提供者

public class MyRolesProvider : MembersRoleProvider
{
    const string VISITORS_LABEL = "Visitor";

    public override string[] GetAllRoles()
    {
        var roles = base.GetAllRoles().ToList();
        roles.Add(VISITORS_LABEL);
        return roles.ToArray();
    }
    public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    {
        if(roleName== VISITORS_LABEL)
        {
            var users = ApplicationContext.Current.Services.MemberService
                .GetAllMembers().Select(m => m.Email).ToList();
            users.Add("tester@test.com");
            return users.ToArray();
        }
        return base.FindUsersInRole(roleName, usernameToMatch);
    }
    public override bool RoleExists(string roleName)
    {
        if(roleName == VISITORS_LABEL)
        {
            return true;
        }
        return base.RoleExists(roleName);
    }
    public override string[] GetRolesForUser(string username)
    {
        var roles = base.GetRolesForUser(username).ToList();
        roles.Add(VISITORS_LABEL);
        return roles.ToArray();
    }
    public override bool IsUserInRole(string username, string roleName)
    {
        if(roleName == VISITORS_LABEL)
        {
            return true;
        }
        return base.IsUserInRole(username, roleName);
    }
    public override string[] GetUsersInRole(string roleName)
    {
        if(roleName == VISITORS_LABEL)
        {
            var list = ApplicationContext.Current.Services.MemberService
                .GetAllMembers().Select(m => m.Email).ToList();
                list.Add("tester@test.com");
            return list.ToArray();
        }
        return base.GetUsersInRole(roleName);
    }
}

编辑3:

我转载了你的场景,跟web.config配置有关

当我保留如下的会员网络配置时,除非我调用

,否则它不会影响我的提供商
<membership defaultProvider="MyMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" />
    <add name="MyMembershipProvider" type="Umbraco724.Providers.MyMembersMembershipProvider, Umbraco724" />
    <add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
  </providers>
</membership>

但是当将其更改为如下所示时,它起作用了。请仔细检查配置的差异。

<membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="UmbracoMembershipProvider" type="Umbraco724.Providers.MyMembersMembershipProvider, Umbraco724"  minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Visitor" passwordFormat="Hashed" />
    <add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
  </providers>
</membership>

我认为 umbraco 只为会员和用户提供一个提供商。此外,当它的名称应该是 UmbracoMembershipProvider 时。当我保持不同时也有错误。