前言
菜鸟去重复之Sql的问题还没有得到满意的答案。如果哪位大哥有相关的资料解释,能够分享给我,那就太谢谢了。
以后每发表一篇博文我都会将以前遗留的问题在前言里指出,直到解决为止。
本文主要在于探讨一下Asp.net Mvc4默认生成的权限的详细内容。
本文篇幅贴的代码有点多,难免枯燥乏味,奈何水平有限,不贴不行,还请见谅!
无可奈何的表名
还记得这张图片不
是不是感觉这些表名看起来很不爽,非要有个webpages前缀。
于是我第一时间想到是不是有方法来设置这些表名。在上篇博客我们已经知道了是
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
这行代码内部创建了上图的四个表。而这个方法内部是如何创建这些表的呢?我也不知道。反编译吧!反编译的代码有点多,我把主要的贴出来然后一步步分析下
private static void InitializeProviders(DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables) { SimpleMembershipProvider simpleMembership = Membership.Provider as SimpleMembershipProvider; if (simpleMembership != null) { InitializeMembershipProvider(simpleMembership, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables); } SimpleRoleProvider provider = Roles.Provider as SimpleRoleProvider; if (provider != null) { InitializeRoleProvider(provider, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables); } Initialized = true; } internal static void InitializeMembershipProvider(SimpleMembershipProvider simpleMembership, DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool createTables) { if (simpleMembership.InitializeCalled) { throw new InvalidOperationException(WebDataResources.Security_InitializeAlreadyCalled); } simpleMembership.ConnectionInfo = connect; simpleMembership.UserIdColumn = userIdColumn; simpleMembership.UserNameColumn = userNameColumn; simpleMembership.UserTableName = userTableName; if (createTables) { simpleMembership.CreateTablesIfNeeded(); } else { simpleMembership.ValidateUserTable(); } simpleMembership.InitializeCalled = true; }
在InitializeDatabaseConnection方法中调用了InitializeProviders方法(里面的SimpleMembership和SimpleRoleProvider后面会说到)。
我们还是直接看26-33行,createTables是bool型,就是我们的autoCreateTables参数,只是换了个变量名。
从上我们可以知道如果设置autoCreateTables为true,调用simpleMembership.CreateTablesIfNeeded方法。反之,调用simpleMembership.ValidateUserTable方法。下来让我们看下我们需要的CreateTablesIfNeeded实现
internal void CreateTablesIfNeeded() { using (IDatabase database = this.ConnectToDatabase()) { if (!CheckTableExists(database, this.UserTableName)) { database.Execute("CREATE TABLE " + this.SafeUserTableName + "(" + this.SafeUserIdColumn + " int NOT NULL PRIMARY KEY IDENTITY, " + this.SafeUserNameColumn + " nvarchar(56) NOT NULL UNIQUE)", new object[0]); } if (!CheckTableExists(database, OAuthMembershipTableName)) { database.Execute("CREATE TABLE " + OAuthMembershipTableName + " (Provider nvarchar(30) NOT NULL, ProviderUserId nvarchar(100) NOT NULL, UserId int NOT NULL, PRIMARY KEY (Provider, ProviderUserId))", new object[0]); } if (!CheckTableExists(database, MembershipTableName)) { database.Execute("CREATE TABLE " + MembershipTableName + " ( UserId int NOT NULL PRIMARY KEY, CreateDate datetime , ConfirmationToken nvarchar(128) , IsConfirmed bit DEFAULT 0, LastPasswordFailureDate datetime , PasswordFailuresSinceLastSuccess int NOT NULL DEFAULT 0, Password nvarchar(128) NOT NULL, PasswordChangedDate datetime , PasswordSalt nvarchar(128) NOT NULL, PasswordVerificationToken nvarchar(128) , PasswordVerificationTokenExpirationDate datetime)", new object[0]); } } }
1-17行 我们一眼就看出来,是这个方法使用Sql创建表,而表名正是其中的几个变量:SafeUserTableName,OAuthMembershipTableName,MembershipTableName。而这几个变量是什么样的!?
private string SafeUserTableName { get { return ("[" + this.UserTableName + "]"); } } public string UserTableName { get; set; } internal static string OAuthMembershipTableName { get { return "webpages_OAuthMembership"; } } internal static string MembershipTableName { get { return "webpages_Membership"; } }
这下我们终于知道了:OAuthMembershipTableName,MembershipTableName是没有Set方法的,而UserTableName可以Set。所以想通过这种途径改表名的想法不可行了。
WebSecurity
在我们创建的带有权限的MVC4项目中,在AccountController里我们见到的很多的WebSecurity,登录注册注销内部都调用了WebSercurity的静态方法。而这些方法又是如何实现的,内部都干了些什么呢?
MSDN上有WebSecurity的解释,其中有这么几句
WebSecurity 类在后台与 ASP.NET 成员资格提供程序交互,后者可完成执行安全任务所需的低级工作。ASP.NET Web Pages 中的默认成员资格提供程序为 SimpleMembershipProvider 类
WebSecurity 类不包括用于创建角色和向用户分配角色的功能。
如果你不想将 WebSecurity 类用于自己的网站,则必须将网站配置为使用标准 ASP.NET 成员资格和角色提供程序。此外,你不得调用 InitializeDatabaseConnection() 方法。将仍然加载 SimpleMembershipProvider 和 SimpleRoleProvider 类,但会将方法和属性调用传递给标准成员资格和角色提供程序。
WebSecurity 和 SimpleMembershipProvider 仅实现哈希选项,该选项被视为这些选项中最安全的选项。因此,WebSecurity 不允许你恢复用户的密码;WebSecurity 限制密码恢复选项的目的,是让你为用户创建新密码。
也就是说其实WebSecurity内部使用的使用的登录注册注销等的一些方法其实是直接调用SimpleMembershipProvider的方法(反编译也可以印证这一点)。
当然WebSecurity和SimpleMembershipProvider还是有一些区别的,有兴趣的童鞋可以点开链接或者反编译自己比较下,此处就多说了。
那我们想要知道登录注册注销的内部方法,就可以直接去看SimpleMemberShipProvider了。
SimpleMemberShipProvider
这里,我们可以看到MSDN里对这个类的解释
WebSecurity 帮助器类是建议用于管理用户(成员资格)帐户、密码和执行其他成员资格任务的方法。
SimpleMembershipProvider 类可以管理成员资格任务;
但是,由于 WebSecurity 提供了一种实现成员资格的更简单方法,因此不建议使用该类。
SimpleMembershipProvider 类旨在供需要对成员资格进程实施更准确控制的开发人员使用
从上我们知道,虽然WebSecurity和SimpleMemebershipProvider功能差不多,但WebSecurity更方便简洁,微软建议使用WebSecurity。如果想有更精确地控制则使用SimpleMembershipProvider。
好吧!我们还是继续我们的分析吧!此处以注册里的创建账户为例。
CreateUserAndAccount(String username, String password, Boolean requireConfirmationToken)创建新的用户配置文件和新的成员资格帐户。
参数解释
userName 用户名 password 密码
requireConfirmationToken (可选)若指定必须确认用户帐户,则为 true;否则为 false。默认值为 false。
返回string 可以发送给用户以确认用户帐户的令牌。
public override string CreateAccount(string userName, string password, bool requireConfirmationToken) { this.VerifyInitialized(); if (password.IsEmpty()) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } string str = Crypto.HashPassword(password); if (str.Length > 0x80) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } if (userName.IsEmpty()) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); } using (IDatabase database = this.ConnectToDatabase()) { int num = GetUserId(database, this.SafeUserTableName, this.SafeUserNameColumn, this.SafeUserIdColumn, userName); if (num == -1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } object obj2 = database.QuerySingle("SELECT COUNT(*) FROM [" + MembershipTableName + "] WHERE UserId = @0", new object[] { num }); if (<CreateAccount>o__SiteContainer22.<>p__Site23 == null) { <CreateAccount>o__SiteContainer22.<>p__Site23 = CallSite<Func<CallSite, object, bool>>.Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(SimpleMembershipProvider), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } if (<CreateAccount>o__SiteContainer22.<>p__Site23.Target(<CreateAccount>o__SiteContainer22.<>p__Site23, ((dynamic) obj2)[0] > 0)) { throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); } string str2 = null; object obj3 = DBNull.Value; if (requireConfirmationToken) { str2 = GenerateToken(); obj3 = str2; } int num2 = 0; if (database.Execute("INSERT INTO [" + MembershipTableName + "] (UserId, [Password], PasswordSalt, IsConfirmed, ConfirmationToken, CreateDate, PasswordChangedDate, PasswordFailuresSinceLastSuccess) VALUES (@0, @1, @2, @3, @4, @5, @5, @6)", new object[] { num, str, string.Empty, !requireConfirmationToken, obj3, DateTime.UtcNow, num2 }) != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } return str2; } }
上面贴出的并不是完整的CreateUserAndAccount方法实现,只是里面调用的一个主要方法。还有一个CreateUserRow方法。
在调用CreateAccount方法之前会调用CreateUserRow方法,在我们自己创建的UserProfile或者自动创建的UserTable中插入我们要创建的UserName(当然支持插入其他字段,有个字典类型参数用来传递我们自己定义的字段)。
然后调用这个CreateAccount方法在自动创建的webpages_Membership表中插入用户的注册名、密码、时间等详细信息(41行)。
我们可以清楚地看到我们创建用户的时候内部是使用的Sql语句执行的。并没有多么复杂的逻辑,就是在自动创建的成员资格表里进行的增删改一系列的操作。
而在我们实际使用(简单方便)的过程中,需要用到这些表映射实体(webpages_Roles、webpages_Memebership等)的地方很少,就算有也可以通过GetUserId,GetAllUser等的一些方法获取需要的集合或者单值。
其实有时想想,既然微软几乎已经实现了我们需要用到的所有功能,那改不改表名已经无所谓了。改了也几乎用不到了。而且这是内置的权限功能,本身就是作为样板。
如果是简单业务,对权限要求不是特别高的,完全可以胜任。
当然对于要求有更完善苛刻权限的,那就必须得自己去实现一套,SimpleMembership里的解释已经说了,还是有些功能它并没有实现的,并不适合。
SimpleRoleProvider
先看下MSDN解释
在 ASP.NET Web Pages 网站中,可以通过使用页面的 Roles 属性管理和测试角色。例如,若要确定用户是否属于特定角色,可以调用 Roles.IsUserInRole 方法。
根据设计,如在所有 ASP.NET 角色提供程序使用的 RoleProvider 基类中定义的那样,SimpleRoleProvider 类并不实现可以在 ASP.NET 角色提供程序中实现的所有功能。如果你的网站需要全部角色提供程序功能,则可以跳过 Web Pages 角色系统的初始化(也就是说,不调用 WebSecurity.InitializeDatabaseConnection()),然后启用标准成员资格和角色提供程序。在这种情况下,对 SimpleRoleProvider 类的调用将传递给标准提供程序(在 SimpleRoleProvider 类文档中称为“前面的提供程序”)。
在我们最初贴出的第一段代码(InitializeProviders)里,我们可以看到两行获取MembershipProvider和RolePorvider的代码。
SimpleMembershipProvider simpleMembership = Membership.Provider as SimpleMembershipProvider;
SimpleRoleProvider provider = Roles.Provider as SimpleRoleProvider;
之前已经提到过提供默认的SimpleRoleProvider和SimpleMemberShip。Memebership和Roles都有一个只包含get方法的Provider(对应的MembershipProvider和RoleProvider)属性。而它们内部的公共静态方法使用的都调用Provider提供的方法。那么初始化默认就是调用SimpleMembership和SimpleRoleProvider。也就是说下面几行代码是等效的。
///直接使用Merbership.Provider SimpleMembershipProvider testProvider = Membership.Provider as SimpleMembershipProvider; testProvider.CreateUserAndAccount(model.UserName, model.Password); //使用WebSecurity WebSecurity.CreateUserAndAccount(model.UserName, model.Password); //如果Membership存在CreateUserAndAccount方法并实现了(其实没有实现) Membership.CreateUserAndAccount(model.UserName, model.Password);
RoleProvider和MembershipProvider使用方式差不多。只是Membership内部很多方法都没有实现,而Roles很多需要用到的都实现了。所以一般使用默认的权限需要用到两个
静态类。一个是Websecurity,一个是Roles。Roles的具体方法大家可以打开链接看,此处就不一一说明了。
MSDN上解释的这句--不调用 WebSecurity.InitializeDatabaseConnection()),然后启用标准成员资格和角色提供程序”。在这种情况下,对 SimpleRoleProvider 类的调用将传递给标准提供程序(在 SimpleRoleProvider 类文档中称为“前面的提供程序”)。是什么意思呢?反编译!!!!
public SimpleRoleProvider(RoleProvider previousProvider) { this._previousProvider = previousProvider; } public override void CreateRole(string roleName) { if (!this.InitializeCalled) { this.PreviousProvider.CreateRole(roleName); } else { using (IDatabase database = this.ConnectToDatabase()) { if (FindRoleId(database, roleName) != -1) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, WebDataResources.SimpleRoleProvider_RoleExists, new object[] { roleName })); } if (database.Execute("INSERT INTO " + RoleTableName + " (RoleName) VALUES (@0)", new object[] { roleName }) != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } } }
1-4行 是SimpleRoleProvider的一个构造方法,可以传入你自己定制的RoleProvider。
8行 this.InitializeCalled就是判断是否初始化过SimpleRoleProvider,本文贴出的第一段代码里面有SimpleMermbership的InitializeCalled设置。
8-26行 你就会蛋疼的发现,它要进行判断来决定是使用你自己定制的RoleProvider还是SimpleRoleProvider。而只要你不调用Websecurity.InitializeDatabaseConnection
或者指定InitializeCalled=true。它就会调用你实现的相关方法。此处我也有点不理解,如果已经自己定制了,直接自己把工作都做完了,还要麻烦地去new SimpleRoleProvider,太蛋疼了吧!我觉得还是直接new 自己的不就行了,还要绕这么个圈子用SimpleRoleProvider。
总结
我确信我写的篇幅有点长了(贴的代码很多,但不贴又不好表述),至少我感觉是这样。
废话了这么多,也不知道大家能从中了解到自己想了解的吗。
呵呵!至少我从头到尾几乎都没有提到如何去实现自己的成员角色控制,这个可能还要看以后在项目中熟练使用了再来分享给大家。
我这里有个链接实现自己的MembershipProvider的,大家可以参考下,如果你觉得微软提供的默认权限不理想的话。
反编译的感觉真的很不错,你可以学到很多东西!
本篇文章与上篇都只是稍微详细的分析了下Asp.net Mvc4的模版权限内容。
我的水平有限,如果文章中有分析不对的地方,还请指出来,大家共同探讨进步哈!
我发现了一条菜鸟奔MVC大牛的资料链接,在这里分享给大家了。
写文章不容易,如果对您有用,那就推荐一下吧!
http://www.cnblogs.com/fatbird/p/Mvc4_defaultWebsecurity.html