我该如何在Asp.NET MVC 4中使用角色?

4
我一直在阅读大量的文章和论坛,但我仍然无法弄清楚... 我正在使用Visual Studio Express 2012 for Web构建一个互联网应用程序,其中包括MVC4+Razor+Entity Framework CodeFirst。
据我所知,使用SimpleMembership在MVC4中管理用户和角色比以前的版本更加简单,应该相当简单。
在我的应用程序中,我需要授权仅特定组的用户(例如,只有管理员可以访问某些页面)。我了解通过向[Authorize]注释传递参数来实现:[Authorize(Roles="Admins")] 但是,如何创建这些角色以及如何将用户添加到它们中呢?
为了要求身份验证,我在控制器方法顶部添加了注释[Authorize](没有参数),而且没有进行任何额外的配置或添加其他内容即可正常工作。此外,当我查看自动创建的数据库时,我看到一个名为webpages_UsersInRoles的表,其中包含UserId和RoleId列。所有这些都使我认为这必须是一个非常简单的任务,因为所有事情似乎都已设置好并准备好使用,但我就是想不出来怎么做;)
到目前为止,我尝试过的(并且没有起作用)是: 我有一个“DataContextDbInitializer”类,它继承自DropCreateDatabaseIfModelChanges。 Seed方法在该类内被重写,并添加了以下内容(我必须导入System.Web.Security):
Membership.CreateUser("user1", "123456");
Roles.CreateRole("Admins");
Roles.AddUserToRole("user1", "Admins");

我还在Web.config文件的标签中插入了这个标签:
<roleManager
enabled="true"
cacheRolesInCookie="true" >
</roleManager>

为了尝试它,我在控制器的一个操作方法上方添加了[Authorize(Roles="Admins")],然后以“admin”的身份登录并尝试访问该方法,但没有成功 :(
我不知道还缺少什么... 如果有人能指导我完成这个过程,我会非常高兴,因为这让我发疯了 :P
谢谢!

你看到这篇文章了吗?http://weblogs.asp.net/scottgu/pages/Recipe_3A00_-Implementing-Role_2D00_Based-Security-with-ASP.NET-2.0-using-Windows-Authentication-and-SQL-Server.aspx - Pablo Claus
谢谢 :) 但是那篇文章使用的是旧版本的ASP.NET,并且基于Windows身份验证,而不是使用Forms的互联网应用程序。这就是我阅读所有内容时遇到的问题:它们都已过时,并且混合了太多东西,而不是直截了当 :( - Floella
2个回答

6
我的猜测是你的Seed方法没有被调用。通过在方法中设置断点并在调试器中运行来验证是否已调用该方法。除非您对应用程序进行了其他更改,否则不会使用EF迁移。默认情况下,数据库将使用InitializeSimpleMembershipAttribute进行初始化。您将看到此属性已添加到Account Controller中,如下面的代码所示。
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
  ....
}

这样只有在访问AccountController时才会调用它。您可以在Filters\InitializeSimpleMembershipAttribute.cs中找到定义。以下是该类的代码片段,可能是为什么没有被调用的线索。

// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();

InitializeSimpleMembershipAttribute 中的大部分代码都是为了处理 MVC 应用程序未使用 Forms 身份验证的情况。如果您始终将使用 Forms 身份验证,可以消除此代码并自己初始化数据库。只需从 AccountController 中删除此属性,并在 Global.asax 的 Application_Start 方法中放置自己的初始化代码。您可以向 Application_Start 方法添加以下内容:

Database.SetInitializer<UsersContext>(new MyDBInitializer());

种子方法看起来像这样
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
      "UserProfile", "UserId", "UserName", autoCreateTables: true);

var roles = (SimpleRoleProvider)Roles.Provider;
var membership = (SimpleMembershipProvider)Membership.Provider;

if (!roles.RoleExists("Admin"))
{
     roles.CreateRole("Admin");
}
if (membership.GetUser("user1", false) == null)
{
     membership.CreateUserAndAccount("user1", "123456");
}
if (!roles.GetRolesForUser("user1").Contains("Admin"))
{
    roles.AddUsersToRoles(new[] { "user1" }, new[] { "Admin" });
} 

请注意,在您的Seed方法中调用WebSecurity.InitializeDatabaseConnection方法来初始化Web Matrix安全数据库。
有关在博客文章中为SimpleMembership添加数据的详细信息,请参阅此处,其中包括示例项目的完整源代码。

感谢您的详细解释 :) 然而,我仍然无法让它工作... 我在Home控制器的About()方法顶部添加了[Authorize(Roles = "Admins")],但当我尝试访问它时,我一直被要求登录。可能是用户没有被认定为“管理员”角色的一部分,但我仍然不知道原因。 - Floella
我在答案的最后更新了一个博客文章链接。这篇博客文章提供了有关如何种子SimpleMembership的更详细描述,并提供完整的源代码下载。 - Kevin Junghans

3
最终,我成功让整个过程运作起来了!
现在我的种子方法看起来像这样:
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

if (!Roles.RoleExists("Admins"))
{
    Roles.CreateRole("Admins");
}
if (!WebSecurity.UserExists("admin"))
{
    WebSecurity.CreateUserAndAccount("admin", "123456");
}
if (!Roles.GetRolesForUser("admin").Contains("Admins"))
{
    Roles.AddUsersToRoles(new[] { "admin" }, new[] { "Admins" });
}
base.Seed(context);

我的Global.asax中的App_Start看起来像这样:

Database.SetInitializer(new DataContextDbInitializer());
DataContext c = new DataContext();
c.Database.Initialize(true);

我不确定这是否真的有用,但我在Web.config中的system.web标签中使用了以下代码:

<roleManager enabled="true" cacheRolesInCookie="true" />

我还从AccountController中移除了[InitializeSimpleMembership],以及AccountModels中的UsersContext类。我将这部分代码移到了自己的上下文类中:

public DbSet<UserProfile> UserProfiles { get; set; }

接下来,为了测试一切是否正常,我在HomeController的About()方法上方使用[Authorize(Roles = "Admins")]注释。如果一切按预期工作,这应该强制我以管理员/123456身份登录才能查看由HomeController/About控制的“关于”页面。然后就可以看到啦;)


这与我的答案有何不同。:( - Kevin Junghans
我不确定,但可能是Database.SetInitializer这一部分吧?我看到这是最不同的地方... 不过,你的回答也帮了我很多,谢谢 :) 我没有足够的声望来投票支持你。 - Floella
@PatriciaMiguel,你应该接受Kevin的回答,因为你的答案和他的非常相似。 - Artiom

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