1

我正在构建一个使用 Umbraco7 的 ASP.Net MVC Web 应用程序来替换旧的 WebForms 网站。

旧的 WebForms 站点用于站点Basic Authentication的某些部分(在 IIS 中的目录级别指定),这些部分指定了具有自己的 Active Directory 的默认 Windows 域。浏览器在相应的页面上请求用户 ID 和密码,后面的代码使用该System.Web.UI.Page.User.Identity属性检索用户信息。

我想在新的 Umbraco 网站上提供类似的体验。

MVC 站点的示例使用Basic Authentication将身份验证和默认域指定为控制器方法的属性,http://www.asp.net/mvc/tutorials/older-versions/security/authenticating-users-with-windows-authentication-cs

Umbraco 似乎没有为其内容页面提供单独的控制器方法,而且我没有找到任何使用Basic Authentication和依赖浏览器来检索凭据的 Umbraco 身份验证示例。

是否可以Basic Authentication在 Umbraco 内容页面上使用并使用浏览器检索凭据?

4

1 回答 1

3

更新答案:

在发布原始答案Combining Forms Authentication and Basic Authentication几个月后,我偶然发现了这个问题。我没有测试过这个解决方案,因为那艘船已经航行了,但它看起来很有希望。

原答案:

据我所知,这个问题的答案是“不”。您无法修改 Umbraco 内容页面的标题,因此您无法告诉浏览器针对给定的 LDAP 服务器进行身份验证。

但是,我能够使用表单身份验证以相同的方式运行(针对 Active Directory 进行身份验证并针对我的数据库进行授权)。下面我包含了在 Umbraco 中获得此身份验证所需的所有代码。

登录页面

登录页面只是一个 Umbraco 视图,使用以下部分视图和表面控制添加到其中@Html.Action("MemberUmbLogin", "MemberUmbLoginSurface")

@model CustomUmbraco.Models.MemberUmbLoginModel

@if (User.Identity.IsAuthenticated)
{
    <p>Logged in: @User.Identity.Name</p>
    <p>@Html.ActionLink("Log out", "MemberUmbLogout", "MemberUmbLoginSurface")</p>
}
else
{
    using (Html.BeginUmbracoForm("MemberUmbLogin", "MemberUmbLoginSurface"))
    {
        @Html.EditorFor(x => Model)
        <input type="submit" />
    }

    <p>@TempData["Status"]</p>
}

登录模式

public class MemberUmbLoginModel
{
    public string Username { get; set; }
    [DataType(DataType.Password)]
    public string Password { get; set; }
    public bool RememberMe { get; set; }
}

表面控制器

public class MemberUmbLoginSurfaceController : SurfaceController
{
    //
    // GET: /MemberUmbLogin/
    [HttpGet]
    [ActionName("MemberUmbLogin")]
    public ActionResult MemberUmbLoginGet()
    {
        return PartialView("MemberUmbLogin", new MemberUmbLoginModel());
    }

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

    [HttpPost]
    [ActionName("MemberUmbLogin")]
    public ActionResult MemberUmbLoginPost(MemberUmbLoginModel model)
    {
        string returnUrl = GetValidReturnUrl(Request.UrlReferrer);

        if (Membership.ValidateUser(model.Username, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe);

            if (Url.IsLocalUrl(returnUrl) && !String.IsNullOrWhiteSpace(returnUrl))
            {
                return Redirect(returnUrl);
            }

            return RedirectToCurrentUmbracoPage();
        }

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

    private static String GetValidReturnUrl(Uri uri)
    {
        string returnUrl = null;

        if (uri != null && !String.IsNullOrWhiteSpace(uri.PathAndQuery) && uri.PathAndQuery.StartsWith("/") &&
            !uri.PathAndQuery.StartsWith("//") && !uri.PathAndQuery.StartsWith("/\\"))
        {
            returnUrl = uri.PathAndQuery;
        }

        return returnUrl;
    }
}

我将自定义 MembershipProvider 与标准 Umbraco 角色提供程序一起使用。我依靠 MembershipProvider 来根据我的非 Umbraco 数据库更新成员的角色,每当他们登录时。然后 MembershipProvider 用适当的组更新成员。

注意:因为我使用的是 Umbraco 的角色提供程序,所以我需要将我的非 Umbraco 数据库中的角色作为“成员组”添加到 Umbraco。

网页配置

<!-- Membership Provider -->
<membership defaultProvider="CustomMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="CustomMembershipProvider" type="CustomUmbraco.MembershipProviders.CustomMembershipProvider" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="NetIDAlias" passwordFormat="Hashed" />
    <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="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
  </providers>
</membership>
<!-- Role Provider -->
<roleManager enabled="true" defaultProvider="UmbracoRoleProvider">
  <providers>
    <clear />
    <add name="UmbracoRoleProvider" type="Umbraco.Web.Security.Providers.MembersRoleProvider" />
  </providers>
</roleManager>

会员提供者

public class CustomMembershipProvider : MembershipProvider
{
    private String GetDomainFreeName(String fullName)
    {
        return fullName.Contains("\\") ? fullName.Substring(fullName.IndexOf("\\") + 1) : fullName;
    }

    public override bool ValidateUser(string username, string password)
    {
        DirectoryEntry directoryEntry = new DirectoryEntry(ConfigurationManager.ConnectionStrings["LDAPConnection"].ConnectionString, username, password);
        DirectorySearcher searcher = new DirectorySearcher(directoryEntry);

        String domainFreeName = GetDomainFreeName(username);
        searcher.Filter = String.Format("(&(objectClass=user)(SAMAccountName={0})(!msExchUserAccountControl=2))", domainFreeName);

        SearchResult result;
        try
        {
            result = searcher.FindOne();
        }
        catch (COMException)
        {
            return false; // authentication failed
        }

        if (result != null)
        {
            NotReallyARoleProvider provider = new NotReallyARoleProvider();
            provider.UpdateUserRoles(domainFreeName);

            Member m = Member.GetMemberFromLoginName(domainFreeName);

            Member.AddMemberToCache(m);

            return true;
        }

        return false;
    }
}

更新用户角色方法

public void UpdateUserRoles(String username)
{
    var groups = this.GetRolesForUser(username); // this is the method that gets the roles for your user.
    this.RemoveUsersFromRoles(new[] { username }, this.GetAllRoles());
    this.AddUsersToRoles(new[] { username }, groups);
}

public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
    foreach (String username in usernames)
    {
        Member m = Member.GetMemberFromLoginName(username);
        if (m == null)
        {
            m = Member.MakeNew(username, MemberType.GetByAlias("Member"), new User(0));
            m.LoginName = username;
        }

        roleNames.ForEach(group => m.AddGroup(MemberGroup.GetByName(group).Id));
        m.Save(true);
    }
}

public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
    foreach (String username in usernames)
    {
        Member m = Member.GetMemberFromLoginName(username);
        if (m == null)
        {
            m = Member.MakeNew(username, MemberType.GetByAlias("Member"), new User(0));
            m.LoginName = username;
        }

        roleNames.ForEach(group => m.RemoveGroup(MemberGroup.GetByName(group).Id));
        m.Save(true);
    }
}

最后,我不使用 ActiveDirectoryMembershipProvider 的原因是因为我无法获得足够的权限来修改帐户。

这个解决方案远非完美,但它对我有用。如果您在登录页面时遇到问题,但它表现得好像您不在正确的组中,或者如果您从 Umbraco 界面中删除了一个成员并且当您调用时他们仍然出现Member.GetMemberFromLoginName(username),那么您可能有用以下行替换您的 Provider 中的会员保存代码一次。

ApplicationContext.Current.Services.MemberService.DeleteMembersOfType(MemberType.GetByAlias("Member").Id);

这将清除存储在 ethereal 存储库中的所有成员。

有了所有这些代码,用户就可以像往常一样选择他们想要访问 Umbraco 后台内容页面的组。

于 2014-06-04T21:55:14.310 回答