Asp mvc 4会员资格和WebSecurity

6

我需要一些建议。目前我正在使用WebSecurity方法来处理所有与账户相关的工作。但它不支持电子邮件唯一性验证,所以我有几个选择:

  1. 编写(子类)一个新的SimpleMembershipProvider,覆盖现有的createuserAndAccount方法以验证电子邮件地址。但我还必须实现登录-注销功能(就像websecurity一样)和其他一些功能。

  2. 在数据库中添加唯一性约束,并在我的代码中捕获它们。但这会使我依赖于数据库。

  3. 这可能有点廉价,但我可以复制/粘贴WebSecurity源代码(因为它是开源的)到一个新类中,并修改createUserAndAccount方法。

还有其他选择吗?目前我倾向于第三种选项,这将是最快的方式。 另外,未来我还需要角色,但我不确定WebSecurity是否支持它们。


如果是新数据库,另一个选项是使用电子邮件作为用户名。默认情况下,用户名是唯一的。 - Igarioshka
2个回答

5
如果是我,我可能会这样做:
首先,假设您正在使用带有Entity Framework或一些数据库连接(ADO,LINQ to SQL等)的SimpleMembership,您将有两个组件:WebSecurity.*方法调用和数据库连接以进行配置文件更改。个人而言,我会向数据库添加CONSTRAINT以确保您的数据是纯净的,但您也可以实现处理此逻辑的成员资格服务。
其次,将它们分组到一个接口中,该接口可以在控制器中引用(例如以下内容):
public interface IMembershipService
{
    Int32 CurrentUserId { get; }
    String CurrentUserName { get; }
    Boolean IsAuthenticated { get; }

    Boolean CreateUserAndAccount(String username, String password, String emailaddress = null);
    Boolean CreateUserAndAccount(String username, string password, out String confirmationToken, String emailaddress = null);
    Boolean Login(String username, String password, Boolean persistCookie = false);
    void Logout();
}

然后,您可以将服务实现为SimpleMembership和数据库连接的混合体。为了保持通用性,我使用了模式,但这也可以是直接的DbContextObjectContext等。我还保持简短,所以请原谅缺少校验和短实现。

public class MembershipService : IMembershipService
{
    protected readonly SimpleMembershipProvider membershiProvider;
    protected readonly SimpleRoleProvider roleProvider;
    protected readonly IRepository<UserProfile> profileRepository;

    public MembershipService(IRepository<UserProfile> profileRepository)
    {
        this.membershipProvider = Membership.Provider as SimpleMembershipProvider;
        this.roleProvider = Role.Provider as SimpleRoleProvider;
        this.profileRepository = userRepository;
    }

    #region IMembershipService Implementation

    public Int32 CurrentUserId
    {
        get { return WebSecurity.CurrentUserId; }
    }
    public String CurrentUserName
    {
        get { return WebSecurity.CurrentUserName; }
    }
    public Boolean IsAuthenticated
    {
        get { return WebSecurity.IsAuthenticated; }
    }

    public Boolean CreateUserAndAccount(String username, String password, String emailaddress = null)
    {
        // validate the email address is unique
        if (!this.profileRepository.Any(x => x.EmailAddress == emailaddress))
        {
            WebSecurity.CreateUserAndAccount(username, password, new
            {
                EmailAddress = emailaddress
            }, createConfirmationToken);
            return true;
        }
        else
        {
            // handle the error how you see fit
            // (maybe even exception?)
            return false;
        }
    }
    public Boolean CreateUserAndAccount(String username, String password, out String confirmationToken, String emailaddress = null, out)
    {
        // validate the email address is unique
        if (this.profileRepository.First(x => x.EmailAddress == emailaddress) == null)
        {
            confirmationToken = WebSecurity.CreateUserAndAccount(username, password, new
            {
                EmailAddress = emailaddress
            }, createConfirmationToken);
            return true;
        }
        else
        {
            // handle the error how you see fit
            // (maybe even exception?)
            confirmationToken = String.Empty;
            return false;
        }
    }
    public Boolean Login(String username, String password, Boolean persistCookie = false)
    {
        return WebSecurity.Login(username, password, persistCookie);
    }
    public void Logout()
    {
        WebSecurity.Logout();
    }

    #endregion
}

现在您可以在控制器中引用此接口,并将逻辑放在一个地方。如果您使用 DI 容器,请注册它,但这里是一个示例实现:
public class AccountController: Controller
{
    private readonly IMembershipService membershipService;

    public AccountController(IMembershipService membershipService)
    {
        this.membershipService = membershipService;
    }

    /* ... */

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult Register(LoginViewModel model, String returnUrl)
    {
        if (ModelState.IsValid)
        {
            if (this.membershipService.CreateUserandAccount(model.Username, model.Password, model.EmailAddress))
            {
                this.membershipService.Login(model.Username, model.Password);
                if (!String.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                return RedirectToRoute("Default");
            }
            else
            {
                ModelState.AddModelError("", "Unable to register.");
            }
        }
        return View(model);
    }

    /* ... */
}

如果您正在使用EntityFramework,您也可以使用IValidatableObject。为了避免重复,这里有另一个SO问题/答案来检查唯一条目:

Entity Framework IValidatableObject


喜欢它!这是一个非常好的解决方案。 - Cristiano Coelho

0

使用第一种选项 1. 让您的自定义成员资格从ExtendedMemberShipprovider继承 AccountMembershipProvider : ExtendedMembershipProvider

使用WebSecurity包装器进行单元测试
示例:
``` public interface IWebSecurity { bool Login(string userName, string password, bool rememberMe = false); bool ChangePassword(string userName, string currentnPassword, string newPassword); void LogOut(); } public class WebSecurityWrapper : IWebSecurity { public bool Login(string userName, string password, bool rememberMe) { return WebSecurity.Login(userName, password, rememberMe); }
public bool ChangePassword(string userName, string currentPassword, string newPassword) { return WebSecurity.ChangePassword(userName, currentPassword, newPassword); }
public void LogOut() { WebSecurity.Logout(); } } ```

在这里,您可以添加您的电子邮件并更改任何方法

  1. 在您的web.config中将您的成员提供程序注册为默认提供程序

我同意创建接口,但你可以在实现中轻松地实现唯一的电子邮件限制。但我也坚信这种约束应该在数据库层面完成,并向Web项目传递。 - Brad Christie
好的,这是一个验证问题。我会先进行前端验证,就像你说的一样,也会在数据库端进行验证。 - Dan Hunex
实现ExtendedMembershipProvider需要太多的工作,仅仅为了一个额外的功能,因为我必须实现所有的方法,而通过扩展SimpleMembershipProvider,我只需要覆盖注册方法,那不是很容易吗?我也可以在这个新类中添加登录/注销方法。为什么需要实现整个extendermembership呢? - Cristiano Coelho
我扩展了ExtendedMembershipProvider并只实现了我需要的部分。 - Dan Hunex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接