如何在 ASP.NET 核心中创建角色并分配给用户?

我使用 ASP.NET 核心默认网站模板,并有身份验证选择为“个人用户帐户”。如何创建角色并将其分配给用户,以便使用控制器中的角色来过滤访问?

155646 次浏览

My comment was deleted because I provided a link to a similar question I answered here. Ergo, I'll answer it more descriptively this time. Here goes.

You could do this easily by creating a CreateRoles method in your startup class. This helps check if the roles are created, and creates the roles if they aren't; on application startup. Like so.

private async Task CreateRoles(IServiceProvider serviceProvider)
{
//initializing custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "Manager", "Member" };
IdentityResult roleResult;


foreach (var roleName in roleNames)
{
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
//create the roles and seed them to the database: Question 1
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}


//Here you could create a super user who will maintain the web app
var poweruser = new ApplicationUser
{


UserName = Configuration["AppSettings:UserName"],
Email = Configuration["AppSettings:UserEmail"],
};
//Ensure you have these values in your appsettings.json file
string userPWD = Configuration["AppSettings:UserPassword"];
var _user = await UserManager.FindByEmailAsync(Configuration["AppSettings:AdminUserEmail"]);


if(_user == null)
{
var createPowerUser = await UserManager.CreateAsync(poweruser, userPWD);
if (createPowerUser.Succeeded)
{
//here we tie the new user to the role
await UserManager.AddToRoleAsync(poweruser, "Admin");


}
}
}

and then you could call the CreateRoles(serviceProvider).Wait(); method from the Configure method in the Startup class. ensure you have IServiceProvider as a parameter in the Configure class.

Using role-based authorization in a controller to filter user access: Question 2

You can do this easily, like so.

[Authorize(Roles="Manager")]
public class ManageController : Controller
{
//....
}

You can also use role-based authorization in the action method like so. Assign multiple roles, if you will

[Authorize(Roles="Admin, Manager")]
public IActionResult Index()
{
/*
.....
*/
}

While this works fine, for a much better practice, you might want to read about using policy based role checks. You can find it on the ASP.NET core documentation here, or this article I wrote about it here

I have created an action in the Accounts controller that calls a function to create the roles and assign the Admin role to the default user. (You should probably remove the default user in production):

    private async Task CreateRolesandUsers()
{
bool x = await _roleManager.RoleExistsAsync("Admin");
if (!x)
{
// first we create Admin rool
var role = new IdentityRole();
role.Name = "Admin";
await _roleManager.CreateAsync(role);


//Here we create a Admin super user who will maintain the website


var user = new ApplicationUser();
user.UserName = "default";
user.Email = "default@default.com";


string userPWD = "somepassword";


IdentityResult chkUser = await _userManager.CreateAsync(user, userPWD);


//Add default User to Role Admin
if (chkUser.Succeeded)
{
var result1 = await _userManager.AddToRoleAsync(user, "Admin");
}
}


// creating Creating Manager role
x = await _roleManager.RoleExistsAsync("Manager");
if (!x)
{
var role = new IdentityRole();
role.Name = "Manager";
await _roleManager.CreateAsync(role);
}


// creating Creating Employee role
x = await _roleManager.RoleExistsAsync("Employee");
if (!x)
{
var role = new IdentityRole();
role.Name = "Employee";
await _roleManager.CreateAsync(role);
}
}

After you could create a controller to manage roles for the users.

Temi's answer is nearly correct, but you cannot call an asynchronous function from a non asynchronous function like he is suggesting. What you need to do is make asynchronous calls in a synchronous function like so :

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();


if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}


app.UseStaticFiles();


app.UseIdentity();


// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715


app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});


CreateRoles(serviceProvider);


}


private void CreateRoles(IServiceProvider serviceProvider)
{


var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
Task<IdentityResult> roleResult;
string email = "someone@somewhere.com";


//Check that there is an Administrator role and create if not
Task<bool> hasAdminRole = roleManager.RoleExistsAsync("Administrator");
hasAdminRole.Wait();


if (!hasAdminRole.Result)
{
roleResult = roleManager.CreateAsync(new IdentityRole("Administrator"));
roleResult.Wait();
}


//Check if the admin user exists and create it if not
//Add to the Administrator role


Task<ApplicationUser> testUser = userManager.FindByEmailAsync(email);
testUser.Wait();


if (testUser.Result == null)
{
ApplicationUser administrator = new ApplicationUser();
administrator.Email = email;
administrator.UserName = email;


Task<IdentityResult> newUser = userManager.CreateAsync(administrator, "_AStrongP@ssword!");
newUser.Wait();


if (newUser.Result.Succeeded)
{
Task<IdentityResult> newUserRole = userManager.AddToRoleAsync(administrator, "Administrator");
newUserRole.Wait();
}
}


}

The key to this is the use of the Task<> class and forcing the system to wait in a slightly different way in a synchronous way.

The following code will work ISA.

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
IServiceProvider serviceProvider)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();


if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}


app.UseStaticFiles();


app.UseIdentity();


// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715


app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});


CreateRolesAndAdminUser(serviceProvider);
}


private static void CreateRolesAndAdminUser(IServiceProvider serviceProvider)
{
const string adminRoleName = "Administrator";
string[] roleNames = { adminRoleName, "Manager", "Member" };


foreach (string roleName in roleNames)
{
CreateRole(serviceProvider, roleName);
}


// Get these value from "appsettings.json" file.
string adminUserEmail = "someone22@somewhere.com";
string adminPwd = "_AStrongP1@ssword!";
AddUserToRole(serviceProvider, adminUserEmail, adminPwd, adminRoleName);
}


/// <summary>
/// Create a role if not exists.
/// </summary>
/// <param name="serviceProvider">Service Provider</param>
/// <param name="roleName">Role Name</param>
private static void CreateRole(IServiceProvider serviceProvider, string roleName)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();


Task<bool> roleExists = roleManager.RoleExistsAsync(roleName);
roleExists.Wait();


if (!roleExists.Result)
{
Task<IdentityResult> roleResult = roleManager.CreateAsync(new IdentityRole(roleName));
roleResult.Wait();
}
}


/// <summary>
/// Add user to a role if the user exists, otherwise, create the user and adds him to the role.
/// </summary>
/// <param name="serviceProvider">Service Provider</param>
/// <param name="userEmail">User Email</param>
/// <param name="userPwd">User Password. Used to create the user if not exists.</param>
/// <param name="roleName">Role Name</param>
private static void AddUserToRole(IServiceProvider serviceProvider, string userEmail,
string userPwd, string roleName)
{
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();


Task<ApplicationUser> checkAppUser = userManager.FindByEmailAsync(userEmail);
checkAppUser.Wait();


ApplicationUser appUser = checkAppUser.Result;


if (checkAppUser.Result == null)
{
ApplicationUser newAppUser = new ApplicationUser
{
Email = userEmail,
UserName = userEmail
};


Task<IdentityResult> taskCreateAppUser = userManager.CreateAsync(newAppUser, userPwd);
taskCreateAppUser.Wait();


if (taskCreateAppUser.Result.Succeeded)
{
appUser = newAppUser;
}
}


Task<IdentityResult> newUserRole = userManager.AddToRoleAsync(appUser, roleName);
newUserRole.Wait();
}

I use this (DI):

public class IdentitySeed
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _rolesManager;
private readonly ILogger _logger;


public IdentitySeed(
ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager,
ILoggerFactory loggerFactory) {
_context = context;
_userManager = userManager;
_rolesManager = roleManager;
_logger = loggerFactory.CreateLogger<IdentitySeed>();
}


public async Task CreateRoles() {
if (await _context.Roles.AnyAsync()) {// not waste time
_logger.LogInformation("Exists Roles.");
return;
}
var adminRole = "Admin";
var roleNames = new String[] { adminRole, "Manager", "Crew", "Guest", "Designer" };


foreach (var roleName in roleNames) {
var role = await _rolesManager.RoleExistsAsync(roleName);
if (!role) {
var result = await _rolesManager.CreateAsync(new ApplicationRole { Name = roleName });
//
_logger.LogInformation("Create {0}: {1}", roleName, result.Succeeded);
}
}
// administrator
var user = new ApplicationUser {
UserName = "Administrator",
Email = "something@something.com",
EmailConfirmed = true
};
var i = await _userManager.FindByEmailAsync(user.Email);
if (i == null) {
var adminUser = await _userManager.CreateAsync(user, "Something*");
if (adminUser.Succeeded) {
await _userManager.AddToRoleAsync(user, adminRole);
//
_logger.LogInformation("Create {0}", user.UserName);
}
}
}
//! By: Luis Harvey Triana Vega
}

In addition to Temi Lajumoke's answer, it's worth noting that after creating the required roles and assigning them to specific users in ASP.NET Core 2.1 MVC Web Application, after launching the application, you may encounter a method error, such as registering or managing an account:

InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UI.Services.IEmailSender' while attempting to activate 'WebApplication.Areas.Identity.Pages.Account.Manage.IndexModel'.

A similar error can be quickly corrected in the ConfigureServices method by adding the AddDefaultUI() method:

services.AddIdentity<IdentityUser, IdentityRole>()
//services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultUI()
    .AddDefaultTokenProviders();

Check

 https://blogs.msdn.microsoft.com/webdev/2018/03/02/aspnetcore-2-1-identity-ui/

and related topic on github:

 https://github.com/aspnet/Docs/issues/6784 for more information.

And for assigning role to specific user could be used IdentityUser class instead of ApplicationUser.

Update in 2020. Here is another way if you prefer.

 IdentityResult res = new IdentityResult();
var _role = new IdentityRole();
_role.Name = role.RoleName;
res = await _roleManager.CreateAsync(_role);
if (!res.Succeeded)
{
foreach (IdentityError er in res.Errors)
{
ModelState.AddModelError(string.Empty, er.Description);
}
ViewBag.UserMessage = "Error Adding Role";
return View();
}
else
{
ViewBag.UserMessage = "Role Added";
return View();
}

In Configure method declare your role manager (Startup)

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, RoleManager<IdentityRole> roleManager)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}


app.UseHttpsRedirection();


app.UseRouting();


app.UseAuthorization();


app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});


Task.Run(()=>this.CreateRoles(roleManager)).Wait();
}




private async Task CreateRoles(RoleManager<IdentityRole> roleManager)
{
foreach (string rol in this.Configuration.GetSection("Roles").Get<List<string>>())
{
if (!await roleManager.RoleExistsAsync(rol))
{
await roleManager.CreateAsync(new IdentityRole(rol));
}
}
}

OPTIONAL - In appsettings.JSON (it depends on you where you wanna get roles from)

{
"Roles": [
"SuperAdmin",
"Admin",
"Employee",
"Customer"
]
}

.net 6 option:

public static class WebApplicationExtensions
{
public static async Task<WebApplication> CreateRolesAsync(this WebApplication app, IConfiguration configuration)
{
using var scope = app.Services.CreateScope();
var roleManager = (RoleManager<IdentityRole>)scope.ServiceProvider.GetService(typeof(RoleManager<IdentityRole>));
var roles = configuration.GetSection("Roles").Get<List<string>>();


foreach (var role in roles)
{
if (!await roleManager.RoleExistsAsync(role))
await roleManager.CreateAsync(new IdentityRole(role));
}


return app;
}
}

In Program.cs add before app.Run()

await app.CreateRolesAsync(builder.Configuration);