【问题标题】:umbraco public access error when authenticated验证时出现 umbraco 公共访问错误
【发布时间】:2015-07-22 06:52:17
【问题描述】:

我在 Umbraco 7 中遇到公共访问问题。

我使用自定义成员资格提供商通过我的 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 中,并且访客角色在管理面板中被检测为角色。

<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);
    }


}

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

我该如何解决?

【问题讨论】:

  • UmbracoContext.Security.CurrentUser 即使我通过了身份验证也会返回 null !这是正确的轨道。我将添加 MemberLoginSurfaceController 以获取更多详细信息。
  • 我的错。我不清楚,我想让前端经过身份验证的用户访问。 UmbracoContext.Security.CurrentUser 是针对后端用户的,对吧?
  • 是的,它适用于后台用户。你的控制器似乎没问题。看看从 Umbraco.MemberIsLoggedOn()Members.GetCurrentMember() 返回的内容 - 也在第 6 行
  • 基本上我认为您的会员没有经过身份验证。请检查上述方法的结果,并检查您的控制器中对Membership.ValidateUser的调用是否返回true
  • 我的会员已通过身份验证。 Membership.ValidateUser 每次都返回 true。我添加了另一张图片以显示当前用户的角色是好的。

标签: asp.net authentication umbraco umbraco7 roleprovider


【解决方案1】:

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

如果您从后端添加成员,您可以轻松添加“访客”角色。

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

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

如您所知,自定义角色提供者和自定义成员资格提供者是齐头并进的。您已经添加了自定义成员资格提供程序并覆盖了 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),如果失败,返回登录页面,之后就是成功GetUser(object providerUserKey, bool userIsOnline) 接到电话。如果返回 null,则显示访问页面不足,否则会调用 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);
    }
}

EDIT3:

我重现了你的场景,和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 时。当我保持不同时,也会出现错误。

【讨论】:

  • 我必须使用自定义会员服务提供商来访问我们的 CRM 数据库,并且我不想复制数据并将它们保存在 umbraco 数据库中。自定义角色提供程序对于自定义成员资格提供程序是必需的。
  • @GiuDo:你说得对,我创建了一个角色提供者并重现了这个问题。如果该自定义角色从 umbraco 后端应用到成员,它工作正常,用户也来自其他来源,它重定向到登录。如果我发现丢失的链接,我会更新你。
  • 更新了我的答案并经过测试,这一切都应该工作
  • 从我的调试中GetUser(string username, bool userIsOnline) 是你有问题的地方。如果有帮助,请查看我的所有文件,更新答案
  • 是的,我也这么想
【解决方案2】:

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

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

当用户的凭据不正确时,您还使用RedirectToCurrentUmbracoPage(),这也会导致完全重定向到登录页面。如果您只是将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();
    }
}

【讨论】:

  • 我尝试了这些功能但没有效果,我添加了登录页面的截图以获取更多详细信息。我认为登录页面重定向与此问题无关,即使我手动输入 url(作为经过身份验证的用户)我页面的内容也是登录页面。
猜你喜欢
  • 2017-01-24
  • 1970-01-01
  • 2019-05-21
  • 1970-01-01
  • 1970-01-01
  • 2018-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多