Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Készítette: Rick Anderson és Joe Audette
Ez az oktatóanyag bemutatja, hogyan hozhat létre ASP.NET Core-webalkalmazást az engedélyezés által védett felhasználói adatokkal. Megjeleníti a hitelesített (regisztrált) felhasználók által létrehozott névjegyek listáját. Három biztonsági csoport létezik:
- A regisztrált felhasználók megtekinthetik az összes jóváhagyott adatot, és szerkeszthetik/törölhetik saját adataikat.
- A vezetők jóváhagyhatják vagy elutasíthatják a kapcsolattartási adatokat. Csak a jóváhagyott névjegyek láthatók a felhasználók számára.
- A rendszergazdák bármilyen adatot jóváhagyhatnak/elutasíthatnak, illetve szerkeszthetik/törölhetnek.
A dokumentum képei nem felelnek meg pontosan a legújabb sablonoknak.
Az alábbi képen Rick (rick@example.com) felhasználó van bejelentkezve. Rick csak a jóváhagyott névjegyeket tekintheti meg, és a névjegyeihez szerkesztheti/törölheti/újakat hozhat létre. Csak a Rick által létrehozott utolsó rekord jeleníti meg a hivatkozások szerkesztését és törlését . A többi felhasználó nem fogja látni az utolsó rekordot, amíg egy felettes vagy rendszergazda nem módosítja a "Jóváhagyott" állapotot.
Az alábbi képen manager@contoso.com be van jelentkezve és a menedzser szerepkörében van:
Az alábbi képen egy kapcsolat kezelőjének részletes nézete látható.
A Jóváhagyás és elutasítás gomb csak a vezetők és rendszergazdák számára jelenik meg.
Az alábbi képen a admin@contoso.com be van jelentkezve, és az adminisztrátori szerepkörben van.
A rendszergazda minden jogosultsággal rendelkezik. Bármely kapcsolatot elolvashat, szerkeszthet vagy törölhet, és módosíthatja a kapcsolatok állapotát.
Az alkalmazás a következő modell Contact:
public class Contact
{
public int ContactId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
A minta a következő engedélyezési kezelőket tartalmazza:
-
ContactIsOwnerAuthorizationHandler: Biztosítja, hogy a felhasználók csak az adataikat szerkeszthessék. -
ContactManagerAuthorizationHandler: Lehetővé teszi a vezetők számára a partnerek jóváhagyását vagy elutasítását. -
ContactAdministratorsAuthorizationHandler: Lehetővé teszi a rendszergazdák számára a névjegyek jóváhagyását vagy elutasítását, valamint a névjegyek szerkesztését/törlését.
Prerequisites
Ez az oktatóanyag speciális. Ismernie kell a következőt:
- ASP.NET Core
- Authentication
- Fiók megerősítése és jelszó helyreállítása
- Authorization
- Entity Framework Core
A kezdő és befejezett alkalmazás
Töltse le a kész alkalmazást. Tesztelje a kész alkalmazást, hogy megismerje annak biztonsági funkcióit.
Tip
A git sparse-checkout használatával csak a minta almappáját lehet letölteni.
Például:
git clone --depth 1 --filter=blob:none https://github.com/dotnet/AspNetCore.Docs.git --sparse
cd AspNetCore.Docs
git sparse-checkout init --cone
git sparse-checkout set aspnetcore/security/authorization/secure-data/samples
A kezdőalkalmazás
Töltse le a kezdőalkalmazást .
Futtassa az alkalmazást, koppintson a ContactManager hivatkozásra, és ellenőrizze, hogy létrehozhat, szerkeszthet és törölhet-e partnereket. A kezdőalkalmazás létrehozásához lásd : A kezdőalkalmazás létrehozása.
Felhasználói adatok védelme
Az alábbi szakaszok a biztonságos felhasználói adatalkalmazás létrehozásának összes fő lépését ismertetik. Hasznos lehet, ha a befejezett projektre hivatkozik.
Kapcsolattartási adatok kötése a felhasználóhoz
A ASP.NET Identity felhasználói azonosítóval biztosíthatja, hogy a felhasználók szerkeszthessék az adataikat, más felhasználói adatokat azonban nem. Hozzáadás OwnerID és ContactStatus a Contact modellhez:
public class Contact
{
public int ContactId { get; set; }
// user ID from AspNetUser table.
public string? OwnerID { get; set; }
public string? Name { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
[DataType(DataType.EmailAddress)]
public string? Email { get; set; }
public ContactStatus Status { get; set; }
}
public enum ContactStatus
{
Submitted,
Approved,
Rejected
}
OwnerID a felhasználó azonosítója az AspNetUser adatbázis táblájából Identity . A Status mező meghatározza, hogy a kapcsolatokat megtekinthetik-e általában a felhasználók.
Hozzon létre egy új migrálást, és frissítse az adatbázist:
dotnet ef migrations add userID_Status
dotnet ef database update
Szerepkör-szolgáltatások hozzáadása Identity
AddRoles hozzáfűzése a szerepkör-szolgáltatások hozzáadásához:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Hitelesített felhasználók megkövetelése
Állítsa be a tartalék engedélyezési szabályzatot a felhasználók hitelesítésének megkövetelésére:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
Az előző kiemelt kód beállítja a tartalék engedélyezési szabályzatot. A tartalék engedélyezési szabályzathoz minden felhasználót hitelesíteni kell, kivéve Razor az oldalakat, a vezérlőket vagy az engedélyezési attribútummal rendelkező műveleti módszereket. Például az Razor lapok, a [AllowAnonymous] vagy [Authorize(PolicyName="MyPolicy")] vezérlők, illetve műveleti módszerek az alkalmazott engedélyezési attribútumot használják a tartalék engedélyezési szabályzat helyett.
RequireAuthenticatedUser hozzáadja a DenyAnonymousAuthorizationRequirement-et az aktuális példányhoz, amely biztosítja, hogy az aktuális felhasználó hitelesített legyen.
A tartalék engedélyezési szabályzat:
- A rendszer minden olyan kérelemre alkalmazza, amely nem határoz meg explicit módon engedélyezési szabályzatot. A végpont-útválasztás által kiszolgált kérések esetében ez minden olyan végpontot is magában foglal, amely nem ad meg engedélyezési attribútumot. Az engedélyezési köztes szoftver ( például statikus fájlok) után más köztes szoftver által kiszolgált kérések esetében ez minden kérésre alkalmazza a szabályzatot.
Ha a tartalék engedélyezési szabályzatot úgy állítja be, hogy a felhasználókat hitelesíteni kell, az védi az újonnan hozzáadott Razor oldalakat és vezérlőket. Az alapértelmezés szerint szükséges engedélyezés biztonságosabb, mint az új vezérlőkre Razor és a Pagesre támaszkodni az [Authorize] attribútum belefoglalásához.
Az AuthorizationOptions osztály AuthorizationOptions.DefaultPolicy-t is tartalmaz. Az DefaultPolicy az a szabályzat, amelyet a [Authorize] attribútummal használnak, ha nincs másik megadva.
[Authorize] nem tartalmaz elnevezett szabályzatot, ellentétben a [Authorize(PolicyName="MyPolicy")].
A szabályzatokkal kapcsolatos további információkért lásd a szabályzatalapú engedélyezést a ASP.NET Core-ban.
Az MVC-vezérlők és Razor Pages esetében az összes felhasználó hitelesítése megkövetelésének alternatív módja egy engedélyezési szűrő hozzáadása.
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using ContactManager.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllers(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
var app = builder.Build();
Az előző kód egy engedélyezési szűrőt használ, a tartalék szabályzat beállítása végpont-útválasztást használ. A tartalék házirend beállítása az elsődleges módszer az összes felhasználó hitelesítésének megkövetelésére.
Adja hozzá az AllowAnonymous-t a IndexPrivacy lapokhoz, hogy a névtelen felhasználók a regisztráció előtt információt szerezzenek a webhelyről:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ContactManager.Pages;
[AllowAnonymous]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
A tesztfiók konfigurálása
Az SeedData osztály két fiókot hoz létre: rendszergazda és felettes. A Titkos kódkezelő eszközzel jelszót állíthat be ezekhez a fiókokhoz. Állítsa be a jelszót a projektnévtárban (a Program.cs könyvtár):
dotnet user-secrets set SeedUserPW <PW>
Ha gyenge jelszót ad meg, a rendszer kivételt jelez a meghíváskor SeedData.Initialize .
Frissítse az alkalmazást a tesztjelszó használatára:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
// Authorization handlers.
builder.Services.AddScoped<IAuthorizationHandler,
ContactIsOwnerAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler,
ContactAdministratorsAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler,
ContactManagerAuthorizationHandler>();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
// requires using Microsoft.Extensions.Configuration;
// Set password with the Secret Manager tool.
// dotnet user-secrets set SeedUserPW <pw>
var testUserPw = builder.Configuration.GetValue<string>("SeedUserPW");
await SeedData.Initialize(services, testUserPw);
}
Hozza létre a tesztfiókokat és frissítse a névjegyeket
Frissítse az Initialize osztály metódusát SeedData a tesztfiókok létrehozásához:
public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
// For sample purposes seed both with the same password.
// Password is set with the following:
// dotnet user-secrets set SeedUserPW <pw>
// The admin user can do anything
var adminID = await EnsureUser(serviceProvider, testUserPw, "admin@contoso.com");
await EnsureRole(serviceProvider, adminID, Constants.ContactAdministratorsRole);
// allowed user can create and edit contacts that they create
var managerID = await EnsureUser(serviceProvider, testUserPw, "manager@contoso.com");
await EnsureRole(serviceProvider, managerID, Constants.ContactManagersRole);
SeedDB(context, adminID);
}
}
private static async Task<string> EnsureUser(IServiceProvider serviceProvider,
string testUserPw, string UserName)
{
var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
var user = await userManager.FindByNameAsync(UserName);
if (user == null)
{
user = new IdentityUser
{
UserName = UserName,
EmailConfirmed = true
};
await userManager.CreateAsync(user, testUserPw);
}
if (user == null)
{
throw new Exception("The password is probably not strong enough!");
}
return user.Id;
}
private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider,
string uid, string role)
{
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
if (roleManager == null)
{
throw new Exception("roleManager null");
}
IdentityResult IR;
if (!await roleManager.RoleExistsAsync(role))
{
IR = await roleManager.CreateAsync(new IdentityRole(role));
}
var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
//if (userManager == null)
//{
// throw new Exception("userManager is null");
//}
var user = await userManager.FindByIdAsync(uid);
if (user == null)
{
throw new Exception("The testUserPw password was probably not strong enough!");
}
IR = await userManager.AddToRoleAsync(user, role);
return IR;
}
Adja hozzá a rendszergazdai felhasználói azonosítót és ContactStatus a névjegyekhez. Az egyik névjegyet "Elküldöttként", a másikat pedig "Elutasítottként" állítsa be. Adja hozzá a felhasználói azonosítót és az állapotot az összes kapcsolathoz. Csak egy kapcsolat jelenik meg:
public static void SeedDB(ApplicationDbContext context, string adminID)
{
if (context.Contact.Any())
{
return; // DB has been seeded
}
context.Contact.AddRange(
new Contact
{
Name = "Debra Garcia",
Address = "1234 Main St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "debra@example.com",
Status = ContactStatus.Approved,
OwnerID = adminID
},
Tulajdonosi, kezelői és rendszergazdai engedélyezési kezelők létrehozása
Hozzon létre egy osztályt ContactIsOwnerAuthorizationHandler az Engedélyezési mappában. A ContactIsOwnerAuthorizationHandler rendszer ellenőrzi, hogy az erőforráson eljáró felhasználóé-e az erőforrás.
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
namespace ContactManager.Authorization
{
public class ContactIsOwnerAuthorizationHandler
: AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
UserManager<IdentityUser> _userManager;
public ContactIsOwnerAuthorizationHandler(UserManager<IdentityUser>
userManager)
{
_userManager = userManager;
}
protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null || resource == null)
{
return Task.CompletedTask;
}
// If not asking for CRUD permission, return.
if (requirement.Name != Constants.CreateOperationName &&
requirement.Name != Constants.ReadOperationName &&
requirement.Name != Constants.UpdateOperationName &&
requirement.Name != Constants.DeleteOperationName )
{
return Task.CompletedTask;
}
if (resource.OwnerID == _userManager.GetUserId(context.User))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
A ContactIsOwnerAuthorizationHandler meghívja a context.Succeed funkciót, ha az aktuálisan hitelesített felhasználó a kontakt tulajdonosa. Az engedélyezési kezelők általában:
- Hívd fel
context.Succeed, amikor a követelmények teljesülnek. - Vissza
Task.CompletedTask, ha a követelmények nem teljesülnek. ElőzetesTask.CompletedTaskvagycontext.Successhívás nélkül acontext.Failvisszatérés sem nem siker, sem nem kudarc; lehetővé teszi más engedélyezési kezelők futtatását.
Ha explicit módon meg kell jelölnie a hibát, hívja meg a context.Fail függvényt.
Az alkalmazás lehetővé teszi a kapcsolattulajdonosok számára a saját adatuk szerkesztését, törlését és létrehozását.
ContactIsOwnerAuthorizationHandler nem kell ellenőriznie a követelményparaméterben átadott műveletet.
Kezelő engedélyezési kezelőjének létrehozása
Hozzon létre egy osztályt ContactManagerAuthorizationHandler az Engedélyezési mappában. A ContactManagerAuthorizationHandler ellenőrzi, hogy az erőforráson eljáró felhasználó vezető-e. Csak a vezetők hagyhatják jóvá vagy utasíthatják el a tartalommódosításokat (új vagy módosított).
using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
namespace ContactManager.Authorization
{
public class ContactManagerAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null || resource == null)
{
return Task.CompletedTask;
}
// If not asking for approval/reject, return.
if (requirement.Name != Constants.ApproveOperationName &&
requirement.Name != Constants.RejectOperationName)
{
return Task.CompletedTask;
}
// Managers can approve or reject.
if (context.User.IsInRole(Constants.ContactManagersRole))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
Rendszergazdai engedélyezési kezelő létrehozása
Hozzon létre egy osztályt ContactAdministratorsAuthorizationHandler az Engedélyezési mappában. Ellenőrzi ContactAdministratorsAuthorizationHandler , hogy az erőforráson eljáró felhasználó rendszergazda-e. A rendszergazda minden műveletet elvégezhet.
using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace ContactManager.Authorization
{
public class ContactAdministratorsAuthorizationHandler
: AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null)
{
return Task.CompletedTask;
}
// Administrators can do anything.
if (context.User.IsInRole(Constants.ContactAdministratorsRole))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
Az engedélyezési kezelők regisztrálása
Az Entity Framework Core-t használó szolgáltatásokat regisztrálni kell a függőséginjektáláshoz a következőt használva AddScoped. A ContactIsOwnerAuthorizationHandler az ASP.NET Core Identity-t használja, amely az Entity Framework Core-ra épül. Regisztrálja a kezelőket a szolgáltatásgyűjteményben, hogy ContactsController elérhetőek legyenek a függőséginjektálás segítségével. Adja hozzá a következő kódot a ConfigureServicesvégéhez:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
// Authorization handlers.
builder.Services.AddScoped<IAuthorizationHandler,
ContactIsOwnerAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler,
ContactAdministratorsAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler,
ContactManagerAuthorizationHandler>();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
// requires using Microsoft.Extensions.Configuration;
// Set password with the Secret Manager tool.
// dotnet user-secrets set SeedUserPW <pw>
var testUserPw = builder.Configuration.GetValue<string>("SeedUserPW");
await SeedData.Initialize(services, testUserPw);
}
ContactAdministratorsAuthorizationHandler és ContactManagerAuthorizationHandler szingletonként lesznek hozzáadva. Egyediek, mert nem használják az EF-t, és minden szükséges információ a Context metódus paraméterében HandleRequirementAsync van.
Támogatás engedélyezése
Ebben a szakaszban frissíti a Razor Pagest, és hozzáad egy műveleti követelményosztályt.
A kontaktműveletek követelményeinek áttekintése
Tekintse át a ContactOperations osztályt. Ez az osztály az alkalmazás által támogatott követelményeket tartalmazza:
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace ContactManager.Authorization
{
public static class ContactOperations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement {Name=Constants.CreateOperationName};
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement {Name=Constants.ReadOperationName};
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement {Name=Constants.UpdateOperationName};
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement {Name=Constants.DeleteOperationName};
public static OperationAuthorizationRequirement Approve =
new OperationAuthorizationRequirement {Name=Constants.ApproveOperationName};
public static OperationAuthorizationRequirement Reject =
new OperationAuthorizationRequirement {Name=Constants.RejectOperationName};
}
public class Constants
{
public static readonly string CreateOperationName = "Create";
public static readonly string ReadOperationName = "Read";
public static readonly string UpdateOperationName = "Update";
public static readonly string DeleteOperationName = "Delete";
public static readonly string ApproveOperationName = "Approve";
public static readonly string RejectOperationName = "Reject";
public static readonly string ContactAdministratorsRole =
"ContactAdministrators";
public static readonly string ContactManagersRole = "ContactManagers";
}
}
Alaposztály létrehozása a névjegylapokhoz Razor
Hozzon létre egy alaposztályt, amely tartalmazza a kapcsolatok Razor oldalakon használt szolgáltatásokat. Az alaposztály egy helyre helyezi az inicializálási kódot:
using ContactManager.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ContactManager.Pages.Contacts
{
public class DI_BasePageModel : PageModel
{
protected ApplicationDbContext Context { get; }
protected IAuthorizationService AuthorizationService { get; }
protected UserManager<IdentityUser> UserManager { get; }
public DI_BasePageModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager) : base()
{
Context = context;
UserManager = userManager;
AuthorizationService = authorizationService;
}
}
}
Az előző kód:
- Hozzáadja a szolgáltatást az
IAuthorizationServiceengedélyezési kezelőkhöz való hozzáféréshez. - Hozzáadja a Identity
UserManagerszolgáltatást. - Adja hozzá a
ApplicationDbContext.
A CreateModel frissítése
Frissítse a létrehozási oldal modelljét:
- Konstruktor az
DI_BasePageModelalaposztály használatához. -
OnPostAsyncmetódus:- Adja hozzá a felhasználói azonosítót a
Contactmodellhez. - Hívja meg az engedélyezési kezelőt, hogy ellenőrizze, hogy a felhasználó rendelkezik-e jogosultsággal a névjegyek létrehozására.
- Adja hozzá a felhasználói azonosítót a
using ContactManager.Authorization;
using ContactManager.Data;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace ContactManager.Pages.Contacts
{
public class CreateModel : DI_BasePageModel
{
public CreateModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Contact.OwnerID = UserManager.GetUserId(User);
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Create);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Context.Contact.Add(Contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Az IndexModel frissítése
Frissítse a metódust, hogy csak a OnGetAsync jóváhagyott névjegyek jelenjenek meg az általános felhasználók számára:
public class IndexModel : DI_BasePageModel
{
public IndexModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public IList<Contact> Contact { get; set; }
public async Task OnGetAsync()
{
var contacts = from c in Context.Contact
select c;
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
// Only approved contacts are shown UNLESS you're authorized to see them
// or you are the owner.
if (!isAuthorized)
{
contacts = contacts.Where(c => c.Status == ContactStatus.Approved
|| c.OwnerID == currentUserId);
}
Contact = await contacts.ToListAsync();
}
}
Az EditModel frissítése
Adjon hozzá egy engedélyezési kezelőt, amely ellenőrzi, hogy a felhasználó tulajdona-e a kontakt. Mivel az erőforrás-engedélyezés ellenőrzése folyamatban van, az [Authorize] attribútum nem elegendő. Az alkalmazás nem fér hozzá az erőforráshoz az attribútumok kiértékelésekor. Az erőforrás-alapú engedélyezésnek imperatívnak kell lennie. Az ellenőrzéseket el kell végezni, miután az alkalmazás hozzáfér az erőforráshoz, akár az oldalmodellbe töltéssel, akár a saját kezelőjébe történő betöltéssel. Az erőforrást gyakran az erőforráskulcs átadásával érheti el.
public class EditModel : DI_BasePageModel
{
public EditModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact? contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
Contact = contact;
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Update);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
if (!ModelState.IsValid)
{
return Page();
}
// Fetch Contact from DB to get OwnerID.
var contact = await Context
.Contact.AsNoTracking()
.FirstOrDefaultAsync(m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, contact,
ContactOperations.Update);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Contact.OwnerID = contact.OwnerID;
Context.Attach(Contact).State = EntityState.Modified;
if (Contact.Status == ContactStatus.Approved)
{
// If the contact is updated after approval,
// and the user cannot approve,
// set the status back to submitted so the update can be
// checked and approved.
var canApprove = await AuthorizationService.AuthorizeAsync(User,
Contact,
ContactOperations.Approve);
if (!canApprove.Succeeded)
{
Contact.Status = ContactStatus.Submitted;
}
}
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
A DeleteModel frissítése
Frissítse a törlési oldalmodellt, hogy az engedélykezelő használatával ellenőrizze, hogy a felhasználó rendelkezik-e törlési engedéllyel a kapcsolaton.
public class DeleteModel : DI_BasePageModel
{
public DeleteModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact? _contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (_contact == null)
{
return NotFound();
}
Contact = _contact;
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Delete);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
var contact = await Context
.Contact.AsNoTracking()
.FirstOrDefaultAsync(m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, contact,
ContactOperations.Delete);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Context.Contact.Remove(contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Az engedélyezési szolgáltatás beszúrása a nézetekbe
Jelenleg a felhasználói felületen olyan névjegyek szerkesztési és törlési hivatkozásai jelennek meg, amelyeket a felhasználó nem tud módosítani.
Szúrja be az engedélyezési szolgáltatást a Pages/_ViewImports.cshtml fájlba, hogy az minden nézet számára elérhető legyen:
@using Microsoft.AspNetCore.Identity
@using ContactManager
@using ContactManager.Data
@namespace ContactManager.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using ContactManager.Authorization;
@using Microsoft.AspNetCore.Authorization
@using ContactManager.Models
@inject IAuthorizationService AuthorizationService
Az előző jelölőnyelv több using kifejezést ad hozzá.
Frissítse a szerkesztési és törlési hivatkozásokat, Pages/Contacts/Index.cshtml hogy azok csak a megfelelő engedélyekkel rendelkező felhasználók számára legyenek megjelenítve:
@page
@model ContactManager.Pages.Contacts.IndexModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Address)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].City)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].State)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Zip)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Status)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Contact) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
@Html.DisplayFor(modelItem => item.City)
</td>
<td>
@Html.DisplayFor(modelItem => item.State)
</td>
<td>
@Html.DisplayFor(modelItem => item.Zip)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@if ((await AuthorizationService.AuthorizeAsync(
User, item,
ContactOperations.Update)).Succeeded)
{
<a asp-page="./Edit" asp-route-id="@item.ContactId">Edit</a>
<text> | </text>
}
<a asp-page="./Details" asp-route-id="@item.ContactId">Details</a>
@if ((await AuthorizationService.AuthorizeAsync(
User, item,
ContactOperations.Delete)).Succeeded)
{
<text> | </text>
<a asp-page="./Delete" asp-route-id="@item.ContactId">Delete</a>
}
</td>
</tr>
}
</tbody>
</table>
Warning
Ha olyan felhasználók elől rejt el hivatkozásokat, akiknek nincs engedélye az adatok módosítására, az nem védi az alkalmazást. A hivatkozások elrejtése felhasználóbarátabbá teszi az alkalmazást, ha csak érvényes hivatkozásokat jelenít meg. A felhasználók feltörhetik a létrehozott URL-címeket, hogy szerkesztési és törlési műveleteket kezdeményezhessenek a nem saját adataikon. Az Razor oldalnak vagy a vezérlőnek hozzáférés-ellenőrzéseket kell végrehajtania az adatok védelméhez.
Frissítés részletei
Frissítse a részletek nézetet, hogy a vezetők jóváhagyhassák vagy elutasíthassák a névjegyeket:
@*Preceding markup omitted for brevity.*@
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Contact.Email)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Contact.Email)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Contact.Status)
</dt>
<dd>
@Html.DisplayFor(model => model.Contact.Status)
</dd>
</dl>
</div>
@if (Model.Contact.Status != ContactStatus.Approved)
{
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact, ContactOperations.Approve)).Succeeded)
{
<form style="display:inline;" method="post">
<input type="hidden" name="id" value="@Model.Contact.ContactId" />
<input type="hidden" name="status" value="@ContactStatus.Approved" />
<button type="submit" class="btn btn-xs btn-success">Approve</button>
</form>
}
}
@if (Model.Contact.Status != ContactStatus.Rejected)
{
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact, ContactOperations.Reject)).Succeeded)
{
<form style="display:inline;" method="post">
<input type="hidden" name="id" value="@Model.Contact.ContactId" />
<input type="hidden" name="status" value="@ContactStatus.Rejected" />
<button type="submit" class="btn btn-xs btn-danger">Reject</button>
</form>
}
}
<div>
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact,
ContactOperations.Update)).Succeeded)
{
<a asp-page="./Edit" asp-route-id="@Model.Contact.ContactId">Edit</a>
<text> | </text>
}
<a asp-page="./Index">Back to List</a>
</div>
A részletek lapmodelljének frissítése
public class DetailsModel : DI_BasePageModel
{
public DetailsModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact? _contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);
if (_contact == null)
{
return NotFound();
}
Contact = _contact;
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
if (!isAuthorized
&& currentUserId != Contact.OwnerID
&& Contact.Status != ContactStatus.Approved)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id, ContactStatus status)
{
var contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var contactOperation = (status == ContactStatus.Approved)
? ContactOperations.Approve
: ContactOperations.Reject;
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, contact,
contactOperation);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
contact.Status = status;
Context.Contact.Update(contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Felhasználó hozzáadása vagy eltávolítása szerepkörhöz
A következő problémával kapcsolatos információkért tekintse meg a következőt :
- Jogosultságok eltávolítása egy felhasználótól. Például elnémít egy felhasználót egy csevegőalkalmazásban.
- Jogosultságok hozzáadása egy felhasználóhoz.
Különbségek a Challenge és a Forbid között
Ez az alkalmazás beállítja az alapértelmezett szabályzatot, hogy hitelesített felhasználókat követeljen meg. Az alábbi kód lehetővé teszi a névtelen hozzáférést. A névtelen felhasználók megjeleníthetik a Challenge és a Forbid közötti különbségeket.
[AllowAnonymous]
public class Details2Model : DI_BasePageModel
{
public Details2Model(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact? _contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);
if (_contact == null)
{
return NotFound();
}
Contact = _contact;
if (!User.Identity!.IsAuthenticated)
{
return Challenge();
}
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
if (!isAuthorized
&& currentUserId != Contact.OwnerID
&& Contact.Status != ContactStatus.Approved)
{
return Forbid();
}
return Page();
}
}
Az előző kódban:
- Ha a felhasználó nincs hitelesítve, a rendszer visszaad egy hibát
ChallengeResult. Ha a rendszer visszaad egyChallengeResulthibát, a rendszer átirányítja a felhasználót a bejelentkezési oldalra. - Ha a felhasználó hitelesítése megtörtént, de nincs engedélyezve, a rendszer ad vissza egy hibát
ForbidResult. Ha a rendszer visszaad egy hibátForbidResult, a rendszer átirányítja a felhasználót a hozzáférés megtagadása lapra.
A kész alkalmazás tesztelése
Warning
Ez a cikk a Titkos kulcskezelő eszközzel tárolja a beszúrt felhasználói fiókok jelszavát. A Secret Manager eszköz a helyi fejlesztés során bizalmas adatok tárolására szolgál. Az alkalmazás tesztelési vagy éles környezetben való üzembe helyezésekor használható hitelesítési eljárásokról további információt a Biztonságos hitelesítési folyamatok című témakörben talál.
Ha még nem állított be jelszót a becsomagolt felhasználói fiókokhoz, a Titkos kódkezelő eszközzel állítsa be a jelszót:
Válasszon erős jelszót:
- Legalább 12 karakter hosszú, de 14 vagy több jobb.
- Nagybetűk, kisbetűk, számok és szimbólumok kombinációja.
- Nem található szó a szótárban, illetve egy személy, karakter, termék vagy szervezet neveként.
- Jelentősen eltér a korábbi jelszavaktól.
- Könnyű neked megjegyezni, de mások számára nehéz kitalálni. Fontolja meg egy olyan emlékezetes kifejezés használatát, mint a "6MonkeysRLooking^".
Hajtsa végre a következő parancsot a projekt mappájából, ahol
<PW>a jelszó található:dotnet user-secrets set SeedUserPW <PW>
Ha az alkalmazás névjegyekkel rendelkezik:
- Törölje a tábla összes rekordját
Contact. - Indítsa újra az alkalmazást az adatbázis üzembe helyezése érdekében.
Az elkészült alkalmazás tesztelésének egyszerű módja három különböző böngésző (vagy inkognitó/InPrivate-munkamenet) elindítása. Egy böngészőben regisztráljon egy új felhasználót (például test@example.com). Jelentkezzen be minden böngészőbe egy másik felhasználóval. Ellenőrizze a következő műveleteket:
- A regisztrált felhasználók megtekinthetik az összes jóváhagyott kapcsolattartási adatot.
- A regisztrált felhasználók szerkeszthetik/törölhetik saját adataikat.
- A vezetők jóváhagyhatják/elutasíthatják a kapcsolattartási adatokat. A
Detailsnézetben a Jóváhagyás és az Elutasítás gomb látható. - A rendszergazdák az összes adatot jóváhagyhatják/elutasíthatják, illetve szerkeszthetik/törölhetik.
| User | Partnerek jóváhagyása vagy elutasítása | Beállítások |
|---|---|---|
| test@example.com | No | Szerkessze és törölje az adatait. |
| manager@contoso.com | Yes | Szerkessze és törölje az adatait. |
| admin@contoso.com | Yes | Az összes adat szerkesztése és törlése. |
Hozzon létre egy kapcsolatot a rendszergazda böngészőjében. Másolja ki a törléshez és szerkesztéshez szükséges URL-címet a rendszergazdával való kapcsolatból. Illessze be ezeket a hivatkozásokat a tesztfelhasználó böngészőjébe, és ellenőrizze, hogy a tesztfelhasználó nem tudja-e végrehajtani ezeket a műveleteket.
A kezdőalkalmazás létrehozása
Razor"ContactManager" nevű Pages-alkalmazás létrehozása
- Hozza létre az alkalmazást egyéni fiókokkal.
- Nevezze el "ContactManager"-nek, így a névtér megegyezik a mintában használt névtérrel.
-
-uldaz SQLite helyett a LocalDB-t adja meg
dotnet new webapp -o ContactManager -au Individual -uldHozzáadás
Models/Contact.cs: secure-data\samples\starter6\ContactManager\Models\Contact.csusing System.ComponentModel.DataAnnotations; namespace ContactManager.Models { public class Contact { public int ContactId { get; set; } public string? Name { get; set; } public string? Address { get; set; } public string? City { get; set; } public string? State { get; set; } public string? Zip { get; set; } [DataType(DataType.EmailAddress)] public string? Email { get; set; } } }Állványozza a
Contactmodellt.Hozza létre a kezdeti migrálást, és frissítse az adatbázist:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet-aspnet-codegenerator razorpage -m Contact -udl -dc ApplicationDbContext -outDir Pages\Contacts --referenceScriptLibraries
dotnet ef database drop -f
dotnet ef migrations add initial
dotnet ef database update
Note
Alapértelmezés szerint a telepíteni kívánt .NET bináris fájlok architektúrája a jelenleg futó operációsrendszer-architektúrát jelöli. Egy másik operációsrendszer-architektúra megadásához lásd: dotnet tool install , --arch option. További információ: GitHub-probléma dotnet/AspNetCore.Docs #29262.
Frissítse a ContactManager horgonyt a
Pages/Shared/_Layout.cshtmlfájlban:<a class="nav-link text-dark" asp-area="" asp-page="/Contacts/Index">Contact Manager</a>Tesztelje az alkalmazást úgy, hogy névjegyet hoz létre, szerkeszt, és töröl
Az adatbázis üzembe helyezése
Adja hozzá a SeedData osztályt az Adatok mappához:
using ContactManager.Models;
using Microsoft.EntityFrameworkCore;
// dotnet aspnet-codegenerator razorpage -m Contact -dc ApplicationDbContext -udl -outDir Pages\Contacts --referenceScriptLibraries
namespace ContactManager.Data
{
public static class SeedData
{
public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw="")
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
SeedDB(context, testUserPw);
}
}
public static void SeedDB(ApplicationDbContext context, string adminID)
{
if (context.Contact.Any())
{
return; // DB has been seeded
}
context.Contact.AddRange(
new Contact
{
Name = "Debra Garcia",
Address = "1234 Main St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "debra@example.com"
},
new Contact
{
Name = "Thorsten Weinrich",
Address = "5678 1st Ave W",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "thorsten@example.com"
},
new Contact
{
Name = "Yuhong Li",
Address = "9012 State st",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "yuhong@example.com"
},
new Contact
{
Name = "Jon Orton",
Address = "3456 Maple St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "jon@example.com"
},
new Contact
{
Name = "Diliana Alexieva-Bosseva",
Address = "7890 2nd Ave E",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "diliana@example.com"
}
);
context.SaveChanges();
}
}
}
Hívás SeedData.Initialize innen Program.cs:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using ContactManager.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
await SeedData.Initialize(services);
}
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Tesztelje, hogy az alkalmazás bevetette-e az adatbázist. Ha a kapcsolati adatbázisban vannak sorok, a seed metódus nem fut.
Ez az oktatóanyag bemutatja, hogyan hozhat létre ASP.NET Core-webalkalmazást az engedélyezés által védett felhasználói adatokkal. Megjeleníti a hitelesített (regisztrált) felhasználók által létrehozott névjegyek listáját. Három biztonsági csoport létezik:
- A regisztrált felhasználók megtekinthetik az összes jóváhagyott adatot, és szerkeszthetik/törölhetik saját adataikat.
- A vezetők jóváhagyhatják vagy elutasíthatják a kapcsolattartási adatokat. Csak a jóváhagyott névjegyek láthatók a felhasználók számára.
- A rendszergazdák bármilyen adatot jóváhagyhatnak/elutasíthatnak, illetve szerkeszthetik/törölhetnek.
A dokumentum képei nem felelnek meg pontosan a legújabb sablonoknak.
Az alábbi képen Rick (rick@example.com) felhasználó van bejelentkezve. Rick csak a jóváhagyott névjegyeket tekintheti meg, és a névjegyeihez szerkesztheti/törölheti/újakat hozhat létre. Csak a Rick által létrehozott utolsó rekord jeleníti meg a hivatkozások szerkesztését és törlését . A többi felhasználó nem fogja látni az utolsó rekordot, amíg egy felettes vagy rendszergazda nem módosítja a "Jóváhagyott" állapotot.
Az alábbi képen manager@contoso.com be van jelentkezve és a menedzser szerepkörében van:
Az alábbi képen egy kapcsolat kezelőjének részletes nézete látható.
A Jóváhagyás és elutasítás gomb csak a vezetők és rendszergazdák számára jelenik meg.
Az alábbi képen a admin@contoso.com be van jelentkezve, és az adminisztrátori szerepkörben van.
A rendszergazda minden jogosultsággal rendelkezik. Elolvashatja, szerkesztheti vagy törölheti a névjegyeket, és módosíthatja a névjegyek állapotát.
Az alkalmazás a következő modell Contact:
public class Contact
{
public int ContactId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
A minta a következő engedélyezési kezelőket tartalmazza:
-
ContactIsOwnerAuthorizationHandler: Biztosítja, hogy a felhasználók csak az adataikat szerkeszthessék. -
ContactManagerAuthorizationHandler: Lehetővé teszi a vezetők számára a partnerek jóváhagyását vagy elutasítását. -
ContactAdministratorsAuthorizationHandler: Lehetővé teszi a rendszergazdák számára a következőt:- Partnerek jóváhagyása vagy elutasítása
- Névjegyek szerkesztése és törlése
Prerequisites
Ez az oktatóanyag speciális. Ismernie kell a következőt:
- ASP.NET Core
- Authentication
- Fiók megerősítése és jelszó helyreállítása
- Authorization
- Entity Framework Core
A kezdő és befejezett alkalmazás
Töltse le a kész alkalmazást. Tesztelje a kész alkalmazást, hogy megismerje annak biztonsági funkcióit.
A kezdőalkalmazás
Töltse le a kezdőalkalmazást .
Futtassa az alkalmazást, koppintson a ContactManager hivatkozásra, és ellenőrizze, hogy létrehozhat, szerkeszthet és törölhet-e partnereket. A kezdőalkalmazás létrehozásához lásd : A kezdőalkalmazás létrehozása.
Felhasználói adatok védelme
Az alábbi szakaszok a biztonságos felhasználói adatalkalmazás létrehozásának összes fő lépését ismertetik. Hasznos lehet, ha a befejezett projektre hivatkozik.
Kapcsolattartási adatok kötése a felhasználóhoz
A ASP.NET Identity felhasználói azonosítóval biztosíthatja, hogy a felhasználók szerkeszthessék az adataikat, más felhasználói adatokat azonban nem. Hozzáadás OwnerID és ContactStatus a Contact modellhez:
public class Contact
{
public int ContactId { get; set; }
// user ID from AspNetUser table.
public string OwnerID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public ContactStatus Status { get; set; }
}
public enum ContactStatus
{
Submitted,
Approved,
Rejected
}
OwnerID a felhasználó azonosítója az AspNetUser adatbázis táblájából Identity . A Status mező meghatározza, hogy a kapcsolatokat megtekinthetik-e általában a felhasználók.
Hozzon létre egy új migrálást, és frissítse az adatbázist:
dotnet ef migrations add userID_Status
dotnet ef database update
Szerepkör-szolgáltatások hozzáadása Identity
AddRoles hozzáfűzése a szerepkör-szolgáltatások hozzáadásához:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Hitelesített felhasználók megkövetelése
Állítsa be a tartalék hitelesítési házirendet a felhasználók hitelesítésének megkövetelésére:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
Az előző kiemelt kód beállítja a biztonsági hitelesítési szabályzatot. A tartalék hitelesítési szabályzathoz minden felhasználót hitelesíteni kell, kivéve Razor az oldalakat, vezérlőket vagy hitelesítési attribútummal rendelkező műveleti módszereket. Például a Razor lapok, vezérlők vagy műveleti metódusok az alkalmazott [AllowAnonymous] vagy [Authorize(PolicyName="MyPolicy")] hitelesítési attribútumot használják a tartalék hitelesítési szabályzat helyett.
RequireAuthenticatedUser hozzáadja a DenyAnonymousAuthorizationRequirement-et az aktuális példányhoz, amely biztosítja, hogy az aktuális felhasználó hitelesített legyen.
A tartalék hitelesítési szabályzat:
- A rendszer minden olyan kérésre alkalmazza, amely nem határoz meg explicit módon hitelesítési szabályzatot. A végpont-útválasztás által kiszolgált kérések esetében ez magában foglal minden olyan végpontot, amely nem ad meg engedélyezési attribútumot. Az engedélyezési köztes szoftver ( például statikus fájlok) után más köztes szoftver által kiszolgált kérések esetén ez a szabályzatot az összes kérelemre alkalmazza.
Ha a tartalék hitelesítési szabályzatot úgy állítja be, hogy a felhasználókat hitelesíteni kell, az védi az újonnan hozzáadott Razor oldalakat és vezérlőket. Ha alapértelmezés szerint hitelesítésre van szükség, az biztonságosabb, mint az új vezérlőkre Razor és a Pagesre támaszkodni az [Authorize] attribútum belefoglalásához.
Az AuthorizationOptions osztály AuthorizationOptions.DefaultPolicy-t is tartalmaz. Az DefaultPolicy az a szabályzat, amelyet a [Authorize] attribútummal használnak, ha nincs másik megadva.
[Authorize] nem tartalmaz elnevezett szabályzatot, ellentétben a [Authorize(PolicyName="MyPolicy")].
A szabályzatokkal kapcsolatos további információkért lásd a szabályzatalapú engedélyezést a ASP.NET Core-ban.
Az MVC-vezérlők és Razor Pages esetében az összes felhasználó hitelesítése megkövetelésének alternatív módja egy engedélyezési szűrő hozzáadása.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddControllers(config =>
{
// using Microsoft.AspNetCore.Mvc.Authorization;
// using Microsoft.AspNetCore.Authorization;
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
Az előző kód egy engedélyezési szűrőt használ, a tartalék szabályzat beállítása végpont-útválasztást használ. A tartalék házirend beállítása az elsődleges módszer az összes felhasználó hitelesítésének megkövetelésére.
Adja hozzá az AllowAnonymous-t a IndexPrivacy lapokhoz, hogy a névtelen felhasználók a regisztráció előtt információt szerezzenek a webhelyről:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace ContactManager.Pages
{
[AllowAnonymous]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}
A tesztfiók konfigurálása
Az SeedData osztály két fiókot hoz létre: rendszergazda és felettes. A Titkos kódkezelő eszközzel jelszót állíthat be ezekhez a fiókokhoz. Állítsa be a jelszót a projektnévtárban (a Program.cs könyvtár):
dotnet user-secrets set SeedUserPW <PW>
Ha nincs megadva erős jelszó, a rendszer kivételt jelez a meghíváskor SeedData.Initialize .
Frissítse Main úgy, hogy a tesztjelszót használja.
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
// requires using Microsoft.Extensions.Configuration;
var config = host.Services.GetRequiredService<IConfiguration>();
// Set password with the Secret Manager tool.
// dotnet user-secrets set SeedUserPW <pw>
var testUserPw = config["SeedUserPW"];
SeedData.Initialize(services, testUserPw).Wait();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Hozza létre a tesztfiókokat és frissítse a névjegyeket
Frissítse az Initialize osztály metódusát SeedData a tesztfiókok létrehozásához:
public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
// For sample purposes seed both with the same password.
// Password is set with the following:
// dotnet user-secrets set SeedUserPW <pw>
// The admin user can do anything
var adminID = await EnsureUser(serviceProvider, testUserPw, "admin@contoso.com");
await EnsureRole(serviceProvider, adminID, Constants.ContactAdministratorsRole);
// allowed user can create and edit contacts that they create
var managerID = await EnsureUser(serviceProvider, testUserPw, "manager@contoso.com");
await EnsureRole(serviceProvider, managerID, Constants.ContactManagersRole);
SeedDB(context, adminID);
}
}
private static async Task<string> EnsureUser(IServiceProvider serviceProvider,
string testUserPw, string UserName)
{
var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
var user = await userManager.FindByNameAsync(UserName);
if (user == null)
{
user = new IdentityUser
{
UserName = UserName,
EmailConfirmed = true
};
await userManager.CreateAsync(user, testUserPw);
}
if (user == null)
{
throw new Exception("The password is probably not strong enough!");
}
return user.Id;
}
private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider,
string uid, string role)
{
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
if (roleManager == null)
{
throw new Exception("roleManager null");
}
IdentityResult IR;
if (!await roleManager.RoleExistsAsync(role))
{
IR = await roleManager.CreateAsync(new IdentityRole(role));
}
var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
//if (userManager == null)
//{
// throw new Exception("userManager is null");
//}
var user = await userManager.FindByIdAsync(uid);
if (user == null)
{
throw new Exception("The testUserPw password was probably not strong enough!");
}
IR = await userManager.AddToRoleAsync(user, role);
return IR;
}
Adja hozzá a rendszergazdai felhasználói azonosítót és ContactStatus a névjegyekhez. Az egyik névjegyet "Elküldöttként", a másikat pedig "Elutasítottként" állítsa be. Adja hozzá a felhasználói azonosítót és az állapotot az összes kapcsolathoz. Csak egy kapcsolat jelenik meg:
public static void SeedDB(ApplicationDbContext context, string adminID)
{
if (context.Contact.Any())
{
return; // DB has been seeded
}
context.Contact.AddRange(
new Contact
{
Name = "Debra Garcia",
Address = "1234 Main St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "debra@example.com",
Status = ContactStatus.Approved,
OwnerID = adminID
},
Tulajdonosi, kezelői és rendszergazdai engedélyezési kezelők létrehozása
Hozzon létre egy osztályt ContactIsOwnerAuthorizationHandler az Engedélyezési mappában. A ContactIsOwnerAuthorizationHandler rendszer ellenőrzi, hogy az erőforráson eljáró felhasználóé-e az erőforrás.
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
namespace ContactManager.Authorization
{
public class ContactIsOwnerAuthorizationHandler
: AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
UserManager<IdentityUser> _userManager;
public ContactIsOwnerAuthorizationHandler(UserManager<IdentityUser>
userManager)
{
_userManager = userManager;
}
protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null || resource == null)
{
return Task.CompletedTask;
}
// If not asking for CRUD permission, return.
if (requirement.Name != Constants.CreateOperationName &&
requirement.Name != Constants.ReadOperationName &&
requirement.Name != Constants.UpdateOperationName &&
requirement.Name != Constants.DeleteOperationName )
{
return Task.CompletedTask;
}
if (resource.OwnerID == _userManager.GetUserId(context.User))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
A ContactIsOwnerAuthorizationHandler meghívja a context.Succeed funkciót, ha az aktuálisan hitelesített felhasználó a kontakt tulajdonosa. Az engedélyezési kezelők általában:
- Hívd fel
context.Succeed, amikor a követelmények teljesülnek. - Vissza
Task.CompletedTask, ha a követelmények nem teljesülnek. ElőzetesTask.CompletedTaskvagycontext.Successhívás nélkül acontext.Failvisszatérés sem nem siker, sem nem kudarc; lehetővé teszi más engedélyezési kezelők futtatását.
Ha explicit módon meg kell jelölnie a hibát, hívja meg a context.Fail függvényt.
Az alkalmazás lehetővé teszi a kapcsolattulajdonosok számára a saját adatuk szerkesztését, törlését és létrehozását.
ContactIsOwnerAuthorizationHandler nem kell ellenőriznie a követelményparaméterben átadott műveletet.
Kezelő engedélyezési kezelőjének létrehozása
Hozzon létre egy osztályt ContactManagerAuthorizationHandler az Engedélyezési mappában. A ContactManagerAuthorizationHandler ellenőrzi, hogy az erőforráson eljáró felhasználó vezető-e. Csak a vezetők hagyhatják jóvá vagy utasíthatják el a tartalommódosításokat (új vagy módosított).
using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
namespace ContactManager.Authorization
{
public class ContactManagerAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null || resource == null)
{
return Task.CompletedTask;
}
// If not asking for approval/reject, return.
if (requirement.Name != Constants.ApproveOperationName &&
requirement.Name != Constants.RejectOperationName)
{
return Task.CompletedTask;
}
// Managers can approve or reject.
if (context.User.IsInRole(Constants.ContactManagersRole))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
Rendszergazdai engedélyezési kezelő létrehozása
Hozzon létre egy osztályt ContactAdministratorsAuthorizationHandler az Engedélyezési mappában. Ellenőrzi ContactAdministratorsAuthorizationHandler , hogy az erőforráson eljáró felhasználó rendszergazda-e. A rendszergazda minden műveletet elvégezhet.
using System.Threading.Tasks;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace ContactManager.Authorization
{
public class ContactAdministratorsAuthorizationHandler
: AuthorizationHandler<OperationAuthorizationRequirement, Contact>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Contact resource)
{
if (context.User == null)
{
return Task.CompletedTask;
}
// Administrators can do anything.
if (context.User.IsInRole(Constants.ContactAdministratorsRole))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
Az engedélyezési kezelők regisztrálása
Az Entity Framework Core-t használó szolgáltatásokat regisztrálni kell a függőséginjektáláshoz a következőt használva AddScoped. A ContactIsOwnerAuthorizationHandler az ASP.NET Core Identity-t használja, amely az Entity Framework Core-ra épül. Regisztrálja a kezelőket a szolgáltatásgyűjteményben, hogy ContactsController elérhetőek legyenek a függőséginjektálás segítségével. Adja hozzá a következő kódot a ConfigureServicesvégéhez:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
// Authorization handlers.
services.AddScoped<IAuthorizationHandler,
ContactIsOwnerAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler,
ContactAdministratorsAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler,
ContactManagerAuthorizationHandler>();
}
ContactAdministratorsAuthorizationHandler és ContactManagerAuthorizationHandler szingletonként lesznek hozzáadva. Egyediek, mert nem használják az EF-t, és minden szükséges információ a Context metódus paraméterében HandleRequirementAsync van.
Támogatás engedélyezése
Ebben a szakaszban frissíti a Razor Pagest, és hozzáad egy műveleti követelményosztályt.
A kontaktműveletek követelményeinek áttekintése
Tekintse át a ContactOperations osztályt. Ez az osztály az alkalmazás által támogatott követelményeket tartalmazza:
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace ContactManager.Authorization
{
public static class ContactOperations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement {Name=Constants.CreateOperationName};
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement {Name=Constants.ReadOperationName};
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement {Name=Constants.UpdateOperationName};
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement {Name=Constants.DeleteOperationName};
public static OperationAuthorizationRequirement Approve =
new OperationAuthorizationRequirement {Name=Constants.ApproveOperationName};
public static OperationAuthorizationRequirement Reject =
new OperationAuthorizationRequirement {Name=Constants.RejectOperationName};
}
public class Constants
{
public static readonly string CreateOperationName = "Create";
public static readonly string ReadOperationName = "Read";
public static readonly string UpdateOperationName = "Update";
public static readonly string DeleteOperationName = "Delete";
public static readonly string ApproveOperationName = "Approve";
public static readonly string RejectOperationName = "Reject";
public static readonly string ContactAdministratorsRole =
"ContactAdministrators";
public static readonly string ContactManagersRole = "ContactManagers";
}
}
Alaposztály létrehozása a névjegylapokhoz Razor
Hozzon létre egy alaposztályt, amely tartalmazza a kapcsolatok Razor oldalakon használt szolgáltatásokat. Az alaposztály egy helyre helyezi az inicializálási kódot:
using ContactManager.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ContactManager.Pages.Contacts
{
public class DI_BasePageModel : PageModel
{
protected ApplicationDbContext Context { get; }
protected IAuthorizationService AuthorizationService { get; }
protected UserManager<IdentityUser> UserManager { get; }
public DI_BasePageModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager) : base()
{
Context = context;
UserManager = userManager;
AuthorizationService = authorizationService;
}
}
}
Az előző kód:
- Hozzáadja a szolgáltatást az
IAuthorizationServiceengedélyezési kezelőkhöz való hozzáféréshez. - Hozzáadja a Identity
UserManagerszolgáltatást. - Adja hozzá a
ApplicationDbContext.
A CreateModel frissítése
Frissítse a lapmodell-konstruktort az DI_BasePageModel alaposztály használatára:
public class CreateModel : DI_BasePageModel
{
public CreateModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
Frissítse a metódust a következőre: CreateModel.OnPostAsync
- Adja hozzá a felhasználói azonosítót a
Contactmodellhez. - Hívja meg az engedélyezési kezelőt, hogy ellenőrizze, hogy a felhasználó rendelkezik-e jogosultsággal a névjegyek létrehozására.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Contact.OwnerID = UserManager.GetUserId(User);
// requires using ContactManager.Authorization;
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Create);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Context.Contact.Add(Contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Az IndexModel frissítése
Frissítse a metódust, hogy csak a OnGetAsync jóváhagyott névjegyek jelenjenek meg az általános felhasználók számára:
public class IndexModel : DI_BasePageModel
{
public IndexModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public IList<Contact> Contact { get; set; }
public async Task OnGetAsync()
{
var contacts = from c in Context.Contact
select c;
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
// Only approved contacts are shown UNLESS you're authorized to see them
// or you are the owner.
if (!isAuthorized)
{
contacts = contacts.Where(c => c.Status == ContactStatus.Approved
|| c.OwnerID == currentUserId);
}
Contact = await contacts.ToListAsync();
}
}
Az EditModel frissítése
Adjon hozzá egy engedélyezési kezelőt, amely ellenőrzi, hogy a felhasználó tulajdona-e a kontakt. Mivel az erőforrás-engedélyezés ellenőrzése folyamatban van, az [Authorize] attribútum nem elegendő. Az alkalmazás nem fér hozzá az erőforráshoz az attribútumok kiértékelésekor. Az erőforrás-alapú engedélyezésnek imperatívnak kell lennie. Az ellenőrzéseket el kell végezni, miután az alkalmazás hozzáfér az erőforráshoz, akár az oldalmodellbe töltéssel, akár a saját kezelőjébe történő betöltéssel. Az erőforrást gyakran az erőforráskulcs átadásával érheti el.
public class EditModel : DI_BasePageModel
{
public EditModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (Contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Update);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
if (!ModelState.IsValid)
{
return Page();
}
// Fetch Contact from DB to get OwnerID.
var contact = await Context
.Contact.AsNoTracking()
.FirstOrDefaultAsync(m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, contact,
ContactOperations.Update);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Contact.OwnerID = contact.OwnerID;
Context.Attach(Contact).State = EntityState.Modified;
if (Contact.Status == ContactStatus.Approved)
{
// If the contact is updated after approval,
// and the user cannot approve,
// set the status back to submitted so the update can be
// checked and approved.
var canApprove = await AuthorizationService.AuthorizeAsync(User,
Contact,
ContactOperations.Approve);
if (!canApprove.Succeeded)
{
Contact.Status = ContactStatus.Submitted;
}
}
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
A DeleteModel frissítése
Frissítse a törlési oldalmodellt, hogy az engedélykezelő használatával ellenőrizze, hogy a felhasználó rendelkezik-e törlési engedéllyel a kapcsolaton.
public class DeleteModel : DI_BasePageModel
{
public DeleteModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (Contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, Contact,
ContactOperations.Delete);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
var contact = await Context
.Contact.AsNoTracking()
.FirstOrDefaultAsync(m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var isAuthorized = await AuthorizationService.AuthorizeAsync(
User, contact,
ContactOperations.Delete);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
Context.Contact.Remove(contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Az engedélyezési szolgáltatás beszúrása a nézetekbe
Jelenleg a felhasználói felületen olyan névjegyek szerkesztési és törlési hivatkozásai jelennek meg, amelyeket a felhasználó nem tud módosítani.
Szúrja be az engedélyezési szolgáltatást a Pages/_ViewImports.cshtml fájlba, hogy az minden nézet számára elérhető legyen:
@using Microsoft.AspNetCore.Identity
@using ContactManager
@using ContactManager.Data
@namespace ContactManager.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using ContactManager.Authorization;
@using Microsoft.AspNetCore.Authorization
@using ContactManager.Models
@inject IAuthorizationService AuthorizationService
Az előző jelölőnyelv több using kifejezést ad hozzá.
Frissítse a szerkesztési és törlési hivatkozásokat, Pages/Contacts/Index.cshtml hogy azok csak a megfelelő engedélyekkel rendelkező felhasználók számára legyenek megjelenítve:
@page
@model ContactManager.Pages.Contacts.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Address)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].City)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].State)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Zip)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Contact[0].Status)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Contact)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
@Html.DisplayFor(modelItem => item.City)
</td>
<td>
@Html.DisplayFor(modelItem => item.State)
</td>
<td>
@Html.DisplayFor(modelItem => item.Zip)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@if ((await AuthorizationService.AuthorizeAsync(
User, item,
ContactOperations.Update)).Succeeded)
{
<a asp-page="./Edit" asp-route-id="@item.ContactId">Edit</a>
<text> | </text>
}
<a asp-page="./Details" asp-route-id="@item.ContactId">Details</a>
@if ((await AuthorizationService.AuthorizeAsync(
User, item,
ContactOperations.Delete)).Succeeded)
{
<text> | </text>
<a asp-page="./Delete" asp-route-id="@item.ContactId">Delete</a>
}
</td>
</tr>
}
</tbody>
</table>
Warning
Ha olyan felhasználók elől rejt el hivatkozásokat, akiknek nincs engedélye az adatok módosítására, az nem védi az alkalmazást. A hivatkozások elrejtése felhasználóbarátabbá teszi az alkalmazást, ha csak érvényes hivatkozásokat jelenít meg. A felhasználók feltörhetik a létrehozott URL-címeket, hogy szerkesztési és törlési műveleteket kezdeményezhessenek a nem saját adataikon. Az Razor oldalnak vagy a vezérlőnek hozzáférés-ellenőrzéseket kell végrehajtania az adatok védelméhez.
Frissítés részletei
Frissítse a részletek nézetet, hogy a vezetők jóváhagyhassák vagy elutasíthassák a névjegyeket:
@*Precedng markup omitted for brevity.*@
<dt>
@Html.DisplayNameFor(model => model.Contact.Email)
</dt>
<dd>
@Html.DisplayFor(model => model.Contact.Email)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Contact.Status)
</dt>
<dd>
@Html.DisplayFor(model => model.Contact.Status)
</dd>
</dl>
</div>
@if (Model.Contact.Status != ContactStatus.Approved)
{
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact, ContactOperations.Approve)).Succeeded)
{
<form style="display:inline;" method="post">
<input type="hidden" name="id" value="@Model.Contact.ContactId" />
<input type="hidden" name="status" value="@ContactStatus.Approved" />
<button type="submit" class="btn btn-xs btn-success">Approve</button>
</form>
}
}
@if (Model.Contact.Status != ContactStatus.Rejected)
{
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact, ContactOperations.Reject)).Succeeded)
{
<form style="display:inline;" method="post">
<input type="hidden" name="id" value="@Model.Contact.ContactId" />
<input type="hidden" name="status" value="@ContactStatus.Rejected" />
<button type="submit" class="btn btn-xs btn-danger">Reject</button>
</form>
}
}
<div>
@if ((await AuthorizationService.AuthorizeAsync(
User, Model.Contact,
ContactOperations.Update)).Succeeded)
{
<a asp-page="./Edit" asp-route-id="@Model.Contact.ContactId">Edit</a>
<text> | </text>
}
<a asp-page="./Index">Back to List</a>
</div>
Frissítse a részletek lapmodellt:
public class DetailsModel : DI_BasePageModel
{
public DetailsModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);
if (Contact == null)
{
return NotFound();
}
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
if (!isAuthorized
&& currentUserId != Contact.OwnerID
&& Contact.Status != ContactStatus.Approved)
{
return Forbid();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id, ContactStatus status)
{
var contact = await Context.Contact.FirstOrDefaultAsync(
m => m.ContactId == id);
if (contact == null)
{
return NotFound();
}
var contactOperation = (status == ContactStatus.Approved)
? ContactOperations.Approve
: ContactOperations.Reject;
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, contact,
contactOperation);
if (!isAuthorized.Succeeded)
{
return Forbid();
}
contact.Status = status;
Context.Contact.Update(contact);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Felhasználó hozzáadása vagy eltávolítása szerepkörhöz
A következő problémával kapcsolatos információkért tekintse meg a következőt :
- Jogosultságok eltávolítása egy felhasználótól. Például elnémít egy felhasználót egy csevegőalkalmazásban.
- Jogosultságok hozzáadása egy felhasználóhoz.
Különbségek a Challenge és a Forbid között
Ez az alkalmazás beállítja az alapértelmezett szabályzatot, hogy hitelesített felhasználókat követeljen meg. Az alábbi kód lehetővé teszi a névtelen hozzáférést. A névtelen felhasználók megjeleníthetik a Challenge és a Forbid közötti különbségeket.
[AllowAnonymous]
public class Details2Model : DI_BasePageModel
{
public Details2Model(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager)
: base(context, authorizationService, userManager)
{
}
public Contact Contact { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Contact = await Context.Contact.FirstOrDefaultAsync(m => m.ContactId == id);
if (Contact == null)
{
return NotFound();
}
if (!User.Identity.IsAuthenticated)
{
return Challenge();
}
var isAuthorized = User.IsInRole(Constants.ContactManagersRole) ||
User.IsInRole(Constants.ContactAdministratorsRole);
var currentUserId = UserManager.GetUserId(User);
if (!isAuthorized
&& currentUserId != Contact.OwnerID
&& Contact.Status != ContactStatus.Approved)
{
return Forbid();
}
return Page();
}
}
Az előző kódban:
- Ha a felhasználó nincs hitelesítve, a rendszer visszaad egy hibát
ChallengeResult. Ha a rendszer visszaad egyChallengeResulthibát, a rendszer átirányítja a felhasználót a bejelentkezési oldalra. - Ha a felhasználó hitelesítése megtörtént, de nincs engedélyezve, a rendszer ad vissza egy hibát
ForbidResult. Ha a rendszer visszaad egy hibátForbidResult, a rendszer átirányítja a felhasználót a hozzáférés megtagadása lapra.
A kész alkalmazás tesztelése
Ha még nem állított be jelszót a becsomagolt felhasználói fiókokhoz, a Titkos kódkezelő eszközzel állítsa be a jelszót:
Válasszon erős jelszót: Használjon nyolc vagy több karaktert, és legalább egy nagybetűs karaktert, számot és szimbólumot. Például
Passw0rd!megfelel az erős jelszókövetelményeknek.Hajtsa végre a következő parancsot a projekt mappájából, ahol
<PW>a jelszó található:dotnet user-secrets set SeedUserPW <PW>
Ha az alkalmazás névjegyekkel rendelkezik:
- Törölje a tábla összes rekordját
Contact. - Indítsa újra az alkalmazást az adatbázis üzembe helyezése érdekében.
Az elkészült alkalmazás tesztelésének egyszerű módja három különböző böngésző (vagy inkognitó/InPrivate-munkamenet) elindítása. Egy böngészőben regisztráljon egy új felhasználót (például test@example.com). Jelentkezzen be minden böngészőbe egy másik felhasználóval. Ellenőrizze a következő műveleteket:
- A regisztrált felhasználók megtekinthetik az összes jóváhagyott kapcsolattartási adatot.
- A regisztrált felhasználók szerkeszthetik/törölhetik saját adataikat.
- A vezetők jóváhagyhatják/elutasíthatják a kapcsolattartási adatokat. A
Detailsnézetben a Jóváhagyás és az Elutasítás gomb látható. - A rendszergazdák az összes adatot jóváhagyhatják/elutasíthatják, illetve szerkeszthetik/törölhetik.
| User | Az alkalmazás által bevetett | Beállítások |
|---|---|---|
| test@example.com | No | Saját adatok szerkesztése/törlése. |
| manager@contoso.com | Yes | Saját adatok jóváhagyása/elutasítása és szerkesztése/törlése. |
| admin@contoso.com | Yes | Az összes adat jóváhagyása/elvetése és szerkesztése/törlése. |
Hozzon létre egy kapcsolatot a rendszergazda böngészőjében. Másolja ki a törléshez és szerkesztéshez szükséges URL-címet a rendszergazdával való kapcsolatból. Illessze be ezeket a hivatkozásokat a tesztfelhasználó böngészőjébe, és ellenőrizze, hogy a tesztfelhasználó nem tudja-e végrehajtani ezeket a műveleteket.
A kezdőalkalmazás létrehozása
Razor"ContactManager" nevű Pages-alkalmazás létrehozása
- Hozza létre az alkalmazást egyéni fiókokkal.
- Nevezze el "ContactManager"-nek, így a névtér megegyezik a mintában használt névtérrel.
-
-uldaz SQLite helyett a LocalDB-t adja meg
dotnet new webapp -o ContactManager -au Individual -uldAdja hozzá
Models/Contact.cs:public class Contact { public int ContactId { get; set; } public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string Zip { get; set; } [DataType(DataType.EmailAddress)] public string Email { get; set; } }Állványozza a
Contactmodellt.Hozza létre a kezdeti migrálást, és frissítse az adatbázist:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet aspnet-codegenerator razorpage -m Contact -udl -dc ApplicationDbContext -outDir Pages\Contacts --referenceScriptLibraries
dotnet ef database drop -f
dotnet ef migrations add initial
dotnet ef database update
Note
Alapértelmezés szerint a telepíteni kívánt .NET bináris fájlok architektúrája a jelenleg futó operációsrendszer-architektúrát jelöli. Egy másik operációsrendszer-architektúra megadásához lásd: dotnet tool install , --arch option. További információ: GitHub-probléma dotnet/AspNetCore.Docs #29262.
Ha hibát tapasztal a paranccsal, tekintse meg ezt a dotnet aspnet-codegenerator razorpageGitHub-problémát.
- Frissítse a ContactManager horgonyt a
Pages/Shared/_Layout.cshtmlfájlban:
<a class="navbar-brand" asp-area="" asp-page="/Contacts/Index">ContactManager</a>
- Tesztelje az alkalmazást úgy, hogy névjegyet hoz létre, szerkeszt, és töröl
Az adatbázis üzembe helyezése
Adja hozzá a SeedData osztályt az Adatok mappához:
using ContactManager.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
// dotnet aspnet-codegenerator razorpage -m Contact -dc ApplicationDbContext -udl -outDir Pages\Contacts --referenceScriptLibraries
namespace ContactManager.Data
{
public static class SeedData
{
public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
SeedDB(context, "0");
}
}
public static void SeedDB(ApplicationDbContext context, string adminID)
{
if (context.Contact.Any())
{
return; // DB has been seeded
}
context.Contact.AddRange(
new Contact
{
Name = "Debra Garcia",
Address = "1234 Main St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "debra@example.com"
},
new Contact
{
Name = "Thorsten Weinrich",
Address = "5678 1st Ave W",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "thorsten@example.com"
},
new Contact
{
Name = "Yuhong Li",
Address = "9012 State st",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "yuhong@example.com"
},
new Contact
{
Name = "Jon Orton",
Address = "3456 Maple St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "jon@example.com"
},
new Contact
{
Name = "Diliana Alexieva-Bosseva",
Address = "7890 2nd Ave E",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "diliana@example.com"
}
);
context.SaveChanges();
}
}
}
Hívás SeedData.Initialize innen Main:
using ContactManager.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace ContactManager
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
SeedData.Initialize(services, "not used");
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Tesztelje, hogy az alkalmazás bevetette-e az adatbázist. Ha a kapcsolati adatbázisban vannak sorok, a seed metódus nem fut.
További erőforrások
- Oktatóanyag: ASP.NET Core- és Azure SQL Database-alkalmazás létrehozása Azure-alkalmazás Szolgáltatásban
- ASP.NET alapvető engedélyezési tesztkörnyezet. Ez a tesztkörnyezet részletesebben ismerteti az oktatóanyagban bevezetett biztonsági funkciókat.
- Bevezetés az engedélyezésbe a ASP.NET Core-ban
- Egyéni szabályzatalapú engedélyezés