Yetkilendirmeyle ASP.NET Core kullanıcı verileriyle bir web uygulaması oluşturma

Rick Anderson ve Joe Audette tarafından

Bu öğreticide, yetkilendirmeyle korunan kullanıcı ASP.NET Core bir web uygulaması oluşturma hakkında bilgi ve bilgiler ve bilgiler yer amaktadır. Kimliği doğrulanmış (kayıtlı) kullanıcıların oluşturduğu kişilerin listesini görüntüler. Üç güvenlik grubu vardır:

  • Kayıtlı kullanıcılar, onaylanan tüm verileri görüntüleyemez ve kendi verilerini düzenleyebilir/silebilir.
  • Yöneticiler kişi verilerini onaylar veya reddeder. Yalnızca onaylanan kişiler kullanıcılara görünür.
  • Yöneticiler verileri onaylar/reddeder ve düzenleyebilir/silebilir.

Bu belgede yer alan görüntüler, en son şablonlarla tam olarak eşleşmez.

Aşağıdaki görüntüde Rick ( rick@example.com ) kullanıcısı oturum açildi. Rick yalnızca onaylanan kişileri görüntüleme ve Silmeyi / Düzenle Yeni / Oluştur bağlantılarını görüntülemesini sağlar. Yalnızca Rick tarafından oluşturulan son kayıt, Düzenle ve Sil bağlantılarını görüntüler. Yönetici veya yönetici durumu "Onaylandı" olarak değiştirirse diğer kullanıcılar son kaydı görmez.

Rick'in oturum açili olduğunu gösteren ekran görüntüsü

Aşağıdaki görüntüde manager@contoso.com oturum ve yönetici rolündedir:

Oturum ayı manager@contoso.com gösteren ekran görüntüsü

Aşağıdaki görüntüde, ilgili kişinin yöneticilere ilişkin ayrıntılar görünümü yer alenidir:

Yöneticinin kişi görünümü

Onayla ve Reddet düğmeleri yalnızca yöneticiler ve yöneticiler için görüntülenir.

Aşağıdaki admin@contoso.com görüntüde, oturum ve yönetici rolündedir:

Oturum ayı admin@contoso.com gösteren ekran görüntüsü

Yöneticinin tüm ayrıcalıkları vardır. Herhangi bir kişiyi okuyabilir/düzenleyebilir/silebilir ve kişilerin durumunu değiştirebilir.

Uygulama, aşağıdaki modelin iskelesi oluşturularak Contact oluşturulmuştır:

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; }
}

Örnek aşağıdaki yetkilendirme işleyicilerini içerir:

  • ContactIsOwnerAuthorizationHandler: Bir kullanıcının yalnızca verilerini düzenlemesini sağlar.
  • ContactManagerAuthorizationHandler: Yöneticilerin kişileri onaylamasını veya reddetmelerini sağlar.
  • ContactAdministratorsAuthorizationHandler: Yöneticilerin kişileri onaylamasını veya reddetmelerini ve kişileri düzenlemelerini/silmelerini sağlar.

Önkoşullar

Bu öğretici gelişmiş bir öğreticidir. Şunları biliyor olması gerekir:

Başlangıç ve tamamlanmış uygulama

Tamamlanan uygulamayı indirin. Tamamlanan uygulamayı test edin, böylece güvenlik özellikleri hakkında bilgi edinebilirsiniz.

Başlangıç uygulaması

Başlangıç uygulamasını indirin.

Uygulamayı çalıştırın, ContactManager bağlantısına dokunun ve kişi oluşturabilirsiniz, düzenleyebilir ve silebilirsiniz. Başlangıç uygulamasını oluşturmak için bkz. Başlangıç uygulaması oluşturma.

Kullanıcı verilerini güvenli hale toplama

Aşağıdaki bölümlerde, güvenli kullanıcı veri uygulaması oluşturmak için tüm önemli adımlar vardır. Tamamlanan projeye başvurmak yararlı olabilir.

Kişi verilerini kullanıcıya bağlama

Kullanıcıların ASP.NET Identity düzenleyeblerinden emin olmak için kullanıcı kimliğini kullanın, ancak diğer kullanıcı verilerini düzenlemez. Modele OwnerID ContactStatus ve Contact ekleyin:

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 , veritabanındaki tablodan kullanıcının AspNetUser Identity kimliğidir. alanı, Status bir kişinin genel kullanıcılar tarafından görüntülenebilir olup olmadığını belirler.

Yeni bir geçiş oluşturun ve veritabanını güncelleştirin:

dotnet ef migrations add userID_Status
dotnet ef database update

Rol hizmetleri'ne ekleme Identity

Rol hizmetleri eklemek için AddRoles ekleyin:

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>();

Kimliği doğrulanmış kullanıcılar gerektirme

Kullanıcıların kimlik doğrulamasının doğrulanmış olması için geri dönüş kimlik doğrulama ilkesi ayarlayın:

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();
    });

Yukarıdaki vurgulanmış kod, geri dönüş kimlik doğrulama ilkesi ayarlar. Geri dönüş kimlik doğrulama ilkesi, Sayfalar, denetleyiciler veya bir kimlik doğrulama özniteliğine sahip eylem yöntemleri dışında tüm kullanıcıların Razor kimlik doğrulamasına sahip olması gerekir. Örneğin, Sayfalar, denetleyiciler veya geri dönüş kimlik doğrulama ilkesi yerine uygulanan kimlik doğrulama özniteliğini kullanan Razor [AllowAnonymous] veya kullanan eylem [Authorize(PolicyName="MyPolicy")] yöntemleri.

RequireAuthenticatedUser , DenyAnonymousAuthorizationRequirement geçerli kullanıcının kimlik doğrulamasının zorlandığı geçerli örneği ekler.

Geri dönüş kimlik doğrulama ilkesi:

  • Açıkça bir kimlik doğrulama ilkesi belirtmeden tüm isteklere uygulanır. Uç nokta yönlendirme tarafından sunulan istekler için bu, yetkilendirme özniteliği belirtmeden herhangi bir uç noktayı içerir. Statik dosyalar gibi yetkilendirme ara yazılımdan sonra diğer ara yazılım tarafından sunulan istekler içinilke tüm isteklere uygulanır.

Kullanıcıların kimlik doğrulamasını gerektirmek için geri dönüş kimlik doğrulama ilkesi ayarlandıktan sonra yeni eklenen Razor Sayfalar ve denetleyiciler koruma sağlar. Varsayılan olarak kimlik doğrulamasının gerekli olması, özniteliği eklemek için yeni denetleyicilere ve Razor Sayfalar'a güvenmekten daha [Authorize] güvenlidir.

sınıfı AuthorizationOptions da AuthorizationOptions.DefaultPolicy içerir. DefaultPolicy, hiçbir ilke belirtilmedinde [Authorize] özniteliğiyle kullanılan ilkedir. [Authorize] , 'den farklı olarak adlandırılmış bir ilke [Authorize(PolicyName="MyPolicy")] içermez.

İlkeler hakkında daha fazla bilgi için ASP.NET Core'de ilke tabanlı yetkilendirme bkz. .

MVC denetleyicilerinin ve Sayfaların tüm kullanıcıların Razor kimliklerini doğrulamalarını gerektirmenin alternatif bir yolu yetkilendirme filtresi eklemektir:

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));
    });

Yukarıdaki kod bir yetkilendirme filtresi kullanır ve geri dönüş ilkesinde uç nokta yönlendirmesi kullanılır. Geri dönüş ilkesi ayarı, tüm kullanıcıların kimlik doğrulamalarını gerektirmek için tercih edilen yöntemdir.

Anonim kullanıcıların kaydolmadan önce site hakkında bilgi alamaları için ve sayfalarına AllowAnonymous Index Privacy ekleyin:

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()
        {

        }
    }
}

Test hesabını yapılandırma

sınıfı SeedData iki hesap oluşturur: yönetici ve yönetici. Bu hesaplar için parola ayarlamak üzere Gizli Dizi Yöneticisi aracını kullanın. Proje dizininden (Program.cs içeren dizin) parolayı ayarlayın:

dotnet user-secrets set SeedUserPW <PW>

Güçlü bir parola belirtilmezse, çağrıldıkta bir özel SeedData.Initialize durum oluşturur.

Test Main parolasını kullanmak için güncelleştirin:

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>();
            });
}

Test hesaplarını oluşturma ve kişileri güncelleştirme

Test Initialize hesaplarını oluşturmak SeedData için sınıfındaki yöntemini güncelleştirin:

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)
{
    IdentityResult IR = null;
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

    if (roleManager == null)
    {
        throw new Exception("roleManager null");
    }

    if (!await roleManager.RoleExistsAsync(role))
    {
        IR = await roleManager.CreateAsync(new IdentityRole(role));
    }

    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    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;
}

Yönetici kullanıcı kimliğini ve ContactStatus kişilerini ekleyin. Kişilerden birini "Gönderildi" ve bir "Reddedildi" olarak seçin. Tüm kişilerin kullanıcı kimliğini ve durumunu ekleyin. Yalnızca bir kişi gösterilir:

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
        },

Sahip, yönetici ve yönetici yetkilendirme işleyicileri oluşturma

Yetkilendirme ContactIsOwnerAuthorizationHandler klasöründe bir sınıf oluşturun. ContactIsOwnerAuthorizationHandler, bir kaynak üzerinde hareket eden kullanıcının kaynağa sahip olduğunu doğrular.

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;
        }
    }
}

Bağlam ContactIsOwnerAuthorizationHandler çağrısı. Geçerli kimliği doğrulanmış kullanıcı kişi sahibi ise başarılı olur. Yetkilendirme işleyicileri genellikle:

  • Gereksinimler context.Succeed karşı geldiğinde çağrısı.
  • Gereksinimler Task.CompletedTask karşılanamay olduğunda geri dön. veya 'ye önceden çağrı yapmadan dönmek başarılı veya başarısız değildir, diğer yetkilendirme Task.CompletedTask context.Success context.Fail işleyicilerin çalışmasına izin verir.

Açıkça başarısız olması gerekirse bağlamı çağır. başarısız olur.

Uygulama, iletişim sahiplerinin kendi verilerini düzenlemesine/silmesine/oluşturmasına izin verir. ContactIsOwnerAuthorizationHandler gereksinim parametresine geçirilen işlemi denetlemesi gerekmez.

Yönetici yetkilendirme işleyicisi oluşturma

ContactManagerAuthorizationHandler Yetkilendirme klasöründe bir sınıf oluşturun. ContactManagerAuthorizationHandlerKaynak üzerinde davranan kullanıcının bir yönetici olduğunu doğrular. İçerik değişikliklerini yalnızca Yöneticiler onaylayabilir veya reddedebilir (yeni veya değiştirilmiş).

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;
        }
    }
}

Yönetici yetkilendirme işleyicisi oluşturma

ContactAdministratorsAuthorizationHandler Yetkilendirme klasöründe bir sınıf oluşturun. ContactAdministratorsAuthorizationHandlerKaynak üzerinde davranan kullanıcının bir yönetici olduğunu doğrular. Yönetici tüm işlemleri yapabilir.

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;
        }
    }
}

Yetkilendirme işleyicilerini kaydetme

Entity Framework Core kullanan hizmetlerin, Addkapsamlıdırkullanılarak bağımlılık ekleme için kayıtlı olması gerekir. , ContactIsOwnerAuthorizationHandler Identity Entity Framework Core oluşturulan ASP.NET Core kullanır. İşleyicileri hizmet koleksiyonuyla kaydedin, bu sayede ContactsController bağımlılık eklemeüzerinden kullanılabilir. Aşağıdaki kodu sonuna ekleyin ConfigureServices :

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 ve ContactManagerAuthorizationHandler tekton olarak eklenir. Bu, EF kullanmayan ve gereken tüm bilgiler yöntemin parametresinde olduğundan tektonlar vardır Context HandleRequirementAsync .

Destek yetkilendirme

Bu bölümde, Razor sayfaları güncelleştirir ve bir işlem gereksinimleri sınıfı eklersiniz.

İlgili kişi işlemleri gereksinimleri sınıfını gözden geçirin

Sınıfını gözden geçirin ContactOperations . Bu sınıf, uygulamanın desteklediği gereksinimleri içerir:

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";
    }
}

Kişiler sayfaları için temel sınıf oluşturma Razor

Kişiler sayfalarında kullanılan hizmetleri içeren bir temel sınıf oluşturun Razor . Temel sınıf, başlatma kodunu bir konuma koyar:

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;
        } 
    }
}

Yukarıdaki kod:

  • IAuthorizationServiceYetkilendirme işleyicilerine erişim için hizmeti ekler.
  • Hizmeti ekler Identity UserManager .
  • Öğesini ekleyin ApplicationDbContext .

CreateModel 'i Güncelleştir

Sayfa modeli oluşturma oluşturucusunu temel sınıfı kullanacak şekilde güncelleştirin DI_BasePageModel :

public class CreateModel : DI_BasePageModel
{
    public CreateModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

Yöntemi şu CreateModel.OnPostAsync şekilde güncelleştirin:

  • Kullanıcı KIMLIĞINI Contact modele ekleyin.
  • Kullanıcının kişi oluşturma iznine sahip olduğunu doğrulamak için yetkilendirme işleyicisini çağırın.
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");
}

Indexmodel 'i Güncelleştir

OnGetAsyncYöntemi yalnızca onaylanan kişilerin genel kullanıcılara gösterilmesi için güncelleştirin:

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();
    }
}

EditModel güncelleştirme

Kullanıcının kişiye ait olduğunu doğrulamak için bir yetkilendirme işleyicisi ekleyin. Kaynak yetkilendirmesi doğrulandığında, [Authorize] öznitelik yeterli değildir. Öznitelikler değerlendirildiğinde uygulamanın kaynağa erişimi yoktur. Kaynak tabanlı yetkilendirme sağlanmalıdır. Uygulamanın kaynağa erişimi olduğunda, sayfa modeline yükleyerek veya işleyicinin kendisi içine yükleyerek denetimlerin gerçekleştirilmesi gerekir. Kaynak anahtarını geçirerek kaynağa sıkça erişirsiniz.

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");
    }
}

DeleteModel 'i Güncelleştir

Kullanıcının kişide silme iznine sahip olduğunu doğrulamak için, silme sayfası modelini yetkilendirme işleyicisini kullanacak şekilde güncelleştirin.

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");
    }
}

Yetkilendirme hizmetini görünümlere ekleme

Şu anda kullanıcı ARABIRIMI, kullanıcının değiştiremeyeceğiniz kişiler için Düzenle ve Sil bağlantılarını gösterir.

Yetkilendirme hizmetini Sayfalar/_ViewImports. cshtml dosyasına ekleme, bu nedenle tüm görünümlerde kullanılabilir hale gelir:

@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

Yukarıdaki biçimlendirme birkaç deyim ekliyor using .

Sayfalar/kişiler/Index. cshtml 'deki Düzenle ve Sil bağlantılarını yalnızca uygun izinlere sahip kullanıcılar için işlendikleri şekilde güncelleştirin:

@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>

Uyarı

Verileri değiştirme izni olmayan kullanıcıların bağlantılarının gizlenmesi, uygulamanın güvenliğini sağlar. Bağlantıların gizlenmesi, yalnızca geçerli bağlantıları görüntüleyerek uygulamayı daha kolay hale getirir. Kullanıcılar, sahip olmadıkları veriler üzerinde düzenleme ve silme işlemlerini çağırmak için oluşturulan URL 'Leri hacme edebilir. RazorSayfanın veya denetleyicinin, verilerin güvenliğini sağlamak için erişim denetimlerini zorlaması gerekir.

Güncelleştirme ayrıntıları

Yöneticilerin kişileri onaylamasını veya reddedebilmesi için Ayrıntılar görünümünü güncelleştirin:

        @*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>

Ayrıntılar sayfası modelini güncelleştirin:

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");
    }
}

Bir role Kullanıcı ekleme veya rolü kaldırma

Hakkında bilgi için Bu soruna bakın:

  • Bir kullanıcıdan ayrıcalıklar kaldırılıyor. Örneğin, bir sohbet uygulamasındaki kullanıcıyı kapatma.
  • Bir kullanıcıya ayrıcalık ekleme.

Challenge ve Fordeklarasyon arasındaki farklar

Bu uygulama, varsayılan ilkeyi kimliği doğrulanmış kullanıcıları gerektirecekşekilde ayarlar. Aşağıdaki kod anonim kullanıcılara izin verir. Anonim kullanıcılara, çekişme ile Fordeklarasyon arasındaki farkları gösterme izni verilir.

[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();
    }
}

Yukarıdaki kodda:

  • Kullanıcının kimliği doğrulanmadığı zaman, bir ChallengeResult döndürülür. Bir ChallengeResult döndürüldüğünde, Kullanıcı oturum açma sayfasına yönlendirilir.
  • Kullanıcının kimliği doğrulandığında, ancak yetkilendirilmediğinde, bir ForbidResult döndürülür. Bir ForbidResult döndürüldüğünde, Kullanıcı erişim reddedildi sayfasına yönlendirilir.

Tamamlanmış uygulamayı test etme

Önceden oluşturulmuş kullanıcı hesapları için bir parola ayarlamadıysanız, parola ayarlamak için gizli dizi Yöneticisi aracını kullanın:

  • Güçlü bir parola seçin: sekiz veya daha fazla karakter, en az bir büyük harf karakter, sayı ve simge kullanın. Örneğin, Passw0rd! güçlü parola gereksinimlerini karşılar.

  • Aşağıdaki komutu projenin klasöründen çalıştırın, burada <PW> parola olur:

    dotnet user-secrets set SeedUserPW <PW>
    

Uygulamanın kişileri varsa:

  • Tablodaki tüm kayıtları silin Contact .
  • Veritabanını temel alarak uygulamayı yeniden başlatın.

Tamamlanmış uygulamayı test etmenin kolay bir yolu da üç farklı tarayıcı (veya bilito/InPrivate oturumlar) kullanmaktır. Tek bir tarayıcıda yeni bir Kullanıcı kaydedin (örneğin, test@example.com ). Her bir tarayıcıda farklı bir kullanıcıyla oturum açın. Aşağıdaki işlemleri doğrulayın:

  • Kayıtlı kullanıcılar tüm onaylanan iletişim verilerini görüntüleyebilir.
  • Kayıtlı kullanıcılar kendi verilerini düzenleyebilir/silebilir.
  • Yöneticiler, kişi verilerini onaylayabilir/reddedebilir. DetailsGörünüm Onayla ve Reddet düğmelerini gösterir.
  • Yöneticiler tüm verileri onaylayabilir/reddedebilir ve düzenleyebilir/silebilir.
Kullanıcı Uygulama tarafından sağlanan Seçenekler
test@example.com Hayır Kendi verilerini düzenleyin/silin.
manager@contoso.com Yes Kendi verilerini onaylama/reddetme ve düzenleme/silme.
admin@contoso.com Yes Tüm verileri onaylama/reddetme ve düzenleme/silme.

Yöneticinin tarayıcısında bir kişi oluşturun. Yönetici iletişim kutusundan silme ve düzenleme için URL 'YI kopyalayın. Bu bağlantıları test kullanıcısının bu işlemleri gerçekleştiremediğinizi doğrulamak için test kullanıcısının tarayıcısına yapıştırın.

Başlangıç uygulamasını oluşturma

  • Razor"ContactManager" adlı bir sayfalar uygulaması oluşturma

    • Ayrı kullanıcı hesaplarıyla uygulamayı oluşturun.
    • Ad alanı örnekte kullanılan ad alanıyla eşleşecek şekilde "ContactManager" olarak adlandırın.
    • -uld SQLite yerine LocalDB belirtir
    dotnet new webapp -o ContactManager -au Individual -uld
    
  • Modeller/Ilgili kişi ekle. 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; }
    }
    
  • Modeli dolandırıcıdan katlayın Contact .

  • İlk geçiş oluşturun ve veritabanını güncelleştirin:

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

komutuyla bir hata yaşarsanız dotnet aspnet-codegenerator razorpage , bu GitHub sorunubölümüne bakın.

  • Pages/Shared/_Layout. cshtml dosyasındaki ContactManager bağlayıcısını güncelleştirin:
<a class="navbar-brand" asp-area="" asp-page="/Contacts/Index">ContactManager</a>
  • Kişiyi oluşturarak, düzenleyerek ve silerek uygulamayı test edin

Veritabanını çekirdek

Veri klasörüne seeddata sınıfını ekleyin:

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();
        }

    }
}

SeedData.InitializeŞuradan arayın 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>();
                });
    }
}

Uygulamanın veritabanını sunan test edin. Kişi VERITABANıNDA herhangi bir satır varsa, çekirdek Yöntem çalıştırılmaz.

bu öğreticide, yetkilendirme tarafından korunan kullanıcı verileriyle ASP.NET Core bir web uygulamasının nasıl oluşturulacağı gösterilmektedir. Kimliği doğrulanmış (kayıtlı) kullanıcıların oluşturduğu kişilerin listesini görüntüler. Üç güvenlik grubu vardır:

  • Kayıtlı kullanıcılar tüm onaylanan verileri görüntüleyebilir ve kendi verilerini düzenleyebilir/silebilir.
  • Yöneticiler , kişi verilerini onaylayabilir veya reddedebilir. Yalnızca onaylanan kişiler kullanıcılara görünür.
  • Yöneticiler , verileri onaylayabilir/reddedebilir ve düzenleyebilir/silebilir.

Aşağıdaki görüntüde, User Rick ( rick@example.com ) oturum açtı. Rick, onaylanan kişileri görüntüleyebilir ve Düzenle / Delete' e / ait kişiler için yeni bağlantılar oluştur ' a bakın. Yalnızca Rick tarafından oluşturulan son kayıt, Düzenle ve Sil bağlantılarını görüntüler. Yönetici veya yönetici durumu "Onaylandı" olarak değiştirene kadar diğer kullanıcılar son kaydı görmez.

Rick oturum açmış olduğunu gösteren ekran görüntüsü

Aşağıdaki görüntüde, manager@contoso.com ve yöneticisinin rolünde oturum açıldı:

Oturum açmış olduğunu gösteren ekran görüntüsü manager@contoso.com

Aşağıdaki görüntüde, bir kişinin Yöneticiler Ayrıntılar görünümü gösterilmektedir:

Yöneticinin bir kişinin görünümü

Onaylama ve reddetme düğmeleri yalnızca Yöneticiler ve yöneticiler için görüntülenir.

Aşağıdaki görüntüde, admin@contoso.com ve yönetici rolünde oturum açıldı:

Oturum açmış olduğunu gösteren ekran görüntüsü admin@contoso.com

Yöneticinin tüm ayrıcalıkları vardır. Herhangi bir kişiyi okuyabilir/düzenleyebilir/silebilir ve kişilerin durumunu değiştirebilir.

Uygulama, aşağıdaki model için Yapı iskelesi tarafından oluşturulmuştur 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; }
}

Örnek, aşağıdaki yetkilendirme işleyicilerini içerir:

  • ContactIsOwnerAuthorizationHandler: Bir kullanıcının yalnızca verilerini düzenleyebilmesini sağlar.
  • ContactManagerAuthorizationHandler: Yöneticilerin kişileri onaylamasını veya reddetmesini sağlar.
  • ContactAdministratorsAuthorizationHandler: Yöneticilerin kişileri onaylamasını veya reddetmesini ve kişileri düzenlemesini/silmesini sağlar.

Önkoşullar

Bu öğretici gelişmiş bir deyişle. Şunu tanımanız gerekir:

Başlatıcı ve tamamlanmış uygulama

Tamamlanmış uygulamayı indirin . Tamamlanmış uygulamayı Test edin, böylece güvenlik özellikleri hakkında bilgi sahibi olun.

Başlangıç uygulaması

Başlangıç uygulamasını indirin .

Uygulamayı çalıştırın, ContactManager bağlantısına dokunun ve bir kişi oluşturup silemdiğinizi doğrulayın.

Güvenli Kullanıcı verileri

Aşağıdaki bölümlerde, güvenli Kullanıcı verileri uygulaması oluşturmak için tüm önemli adımlar verilmiştir. Tamamlanmış projeye başvurmak faydalı olabilir.

Kişi verilerini kullanıcıya bağlama

Identitykullanıcıların verilerini düzenleyebilmeleri, ancak diğer kullanıcıların verilerini düzenleyebilmeleri için ASP.NET kullanıcı kimliğini kullanın. OwnerIDModel ekleyin ContactStatus Contact :

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 kullanıcının AspNetUser veritabanındaki TABLODAKI kimliği Identity . Bu Status alan, bir kişinin genel kullanıcılar tarafından görüntülenebilir olup olmadığını belirler.

Yeni bir geçiş oluşturun ve veritabanını güncelleştirin:

dotnet ef migrations add userID_Status
dotnet ef database update

Rol hizmetlerini Ekle Identity

Rol hizmetleri eklemek için Addroles ekleyin:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

Kimliği doğrulanmış kullanıcılar iste

Kullanıcıların kimliklerinin doğrulanmasını gerektirmek için varsayılan kimlik doğrulama ilkesini ayarlayın:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    })                
       .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Bir Razor sayfada, denetleyicide veya eylem yöntemi düzeyinde kimlik doğrulamasından vazgeçebilirsiniz [AllowAnonymous] . Varsayılan kimlik doğrulama ilkesini kullanıcıların kimliğinin doğrulanmasını gerektirecek şekilde ayarlamak, yeni eklenen Razor sayfaları ve denetleyicileri korur. Varsayılan olarak kimlik doğrulamanın gerekli olması, yeni denetleyicilere ve Razor sayfalarına bağlı olarak, özniteliğini dahil etmek için daha güvenlidir [Authorize] .

Anonim kullanıcıların, kayıt yaptırmadan önce site hakkında bilgi alması için dizine, hakkında ve Iletişim sayfalarına AllowAnonymous ekleyin.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace ContactManager.Pages
{
    [AllowAnonymous]
    public class IndexModel : PageModel
    {
        public void OnGet()
        {

        }
    }
}

Test hesabını yapılandırma

SeedDataSınıfı iki hesap oluşturur: yönetici ve yönetici. Bu hesaplara bir parola ayarlamak için gizli dizi Yöneticisi aracını kullanın. Parolayı proje dizininden ayarlayın ( program. cs içeren dizin):

dotnet user-secrets set SeedUserPW <PW>

Güçlü bir parola belirtilmemişse, çağrıldığında bir özel durum oluşturulur SeedData.Initialize .

MainSınama parolasını kullanacak şekilde güncelleştir:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            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"];
            try
            {
                SeedData.Initialize(services, testUserPw).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex.Message, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Test hesapları oluşturma ve kişileri güncelleştirme

Initialize SeedData Test hesapları oluşturmak için sınıfındaki yöntemi güncelleştirin:

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 };
        await userManager.CreateAsync(user, testUserPw);
    }

    return user.Id;
}

private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider,
                                                              string uid, string role)
{
    IdentityResult IR = null;
    var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

    if (roleManager == null)
    {
        throw new Exception("roleManager null");
    }

    if (!await roleManager.RoleExistsAsync(role))
    {
        IR = await roleManager.CreateAsync(new IdentityRole(role));
    }

    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    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;
}

Yönetici kullanıcı KIMLIĞINI ve kişilerini ekleyin ContactStatus . Kişilerden birini "gönderildi" ve bir "reddedildi" yapın. Kullanıcı KIMLIĞINI ve durumunu tüm kişilere ekleyin. Yalnızca bir kişi gösterilir:

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
        },

Sahip, yönetici ve yönetici yetkilendirme işleyicileri oluşturma

Bir Yetkilendirme klasörü oluşturun ve içinde bir ContactIsOwnerAuthorizationHandler sınıf oluşturun. ContactIsOwnerAuthorizationHandlerKullanıcının kaynağın sahibi olduğunu doğrular.

using System.Threading.Tasks;
using ContactManager.Data;
using ContactManager.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;

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.FromResult(0) if targeting a version of
                // .NET Framework older than 4.6:
                return Task.CompletedTask;
            }

            // If we're 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;
        }
    }
}

ContactIsOwnerAuthorizationHandlerÇağıran bağlamı. Kimliği doğrulanmış geçerli kullanıcı kişi sahibsiyse başarılı olur. Yetkilendirme işleyicileri genellikle:

  • context.SucceedGereksinimler karşılandığında çağırın.
  • Task.CompletedTaskGereksinimler karşılanmazsa döndürün. Task.CompletedTaskVeya için önceki bir çağrı olmadan context.Success döndürme context.Fail işlemi başarılı veya başarısız olmaz, diğer yetkilendirme işleyicilerinin çalışmasına izin verir.

Açıkça başarısız olması gerekiyorsa, bağlamı çağırın. Başarısız oldu.

Uygulama, iletişim sahiplerinin kendi verilerini düzenlemesine/silmesine/oluşturmasına izin verir. ContactIsOwnerAuthorizationHandler gereksinim parametresine geçirilen işlemi denetlemesi gerekmez.

Yönetici yetkilendirme işleyicisi oluşturma

ContactManagerAuthorizationHandler Yetkilendirme klasöründe bir sınıf oluşturun. ContactManagerAuthorizationHandlerKaynak üzerinde davranan kullanıcının bir yönetici olduğunu doğrular. İçerik değişikliklerini yalnızca Yöneticiler onaylayabilir veya reddedebilir (yeni veya değiştirilmiş).

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;
        }
    }
}

Yönetici yetkilendirme işleyicisi oluşturma

Yetkilendirme ContactAdministratorsAuthorizationHandler klasöründe bir sınıf oluşturun. ContactAdministratorsAuthorizationHandler, kaynak üzerinde hareket eden kullanıcının yönetici olduğunu doğrular. Yönetici tüm işlemleri yapar.

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;
        }
    }
}

Yetkilendirme işleyicilerini kaydetme

AddScoped Entity Framework Core kullanan hizmetlerin bağımlılık ekleme için kayıtlı olması gerekir. , ContactIsOwnerAuthorizationHandler ASP.NET Core üzerine Identity inşa edilen Entity Framework Core. İşleyicileri, bağımlılık ekleme ile kullanılabilir olması için hizmet ContactsController koleksiyonuna kaydetme. aşağıdaki kodu sonuna ConfigureServices ekleyin:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc(config =>
    {
        // using Microsoft.AspNetCore.Mvc.Authorization;
        // using Microsoft.AspNetCore.Authorization;
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    })                
       .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    // Authorization handlers.
    services.AddScoped<IAuthorizationHandler,
                          ContactIsOwnerAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactAdministratorsAuthorizationHandler>();

    services.AddSingleton<IAuthorizationHandler,
                          ContactManagerAuthorizationHandler>();
}

ContactAdministratorsAuthorizationHandler ve ContactManagerAuthorizationHandler tek ton olarak eklenir. EF kullanmaları ve gereken tüm bilgiler yönteminin parametresinde olduğu için tek Context HandleRequirementAsync tonlarıdır.

Destek yetkilendirmesi

Bu bölümde Sayfaları güncelleştirin Razor ve bir işlem gereksinimleri sınıfı ekleyin.

Kişi işlemleri gereksinimleri sınıfını gözden geçirme

sınıfını ContactOperations gözden geçirme. Bu sınıf, uygulamanın desteklediği gereksinimleri içerir:

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";
    }
}

Kişiler Sayfaları için temel sınıf Razor oluşturma

Kişi Sayfalarında kullanılan hizmetleri içeren bir temel sınıf Razor oluşturun. Temel sınıf başlatma kodunu tek bir konuma koyar:

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;
        } 
    }
}

Yukarıdaki kod:

  • Yetkilendirme IAuthorizationService işleyicilerine erişmek için hizmeti ekler.
  • Hizmeti Identity UserManager ekler.
  • ekleyin. ApplicationDbContext

CreateModel'i güncelleştirme

Temel sınıfı kullanmak için sayfa modeli oluşturma DI_BasePageModel oluşturucusu güncelleştirildi:

public class CreateModel : DI_BasePageModel
{
    public CreateModel(
        ApplicationDbContext context,
        IAuthorizationService authorizationService,
        UserManager<IdentityUser> userManager)
        : base(context, authorizationService, userManager)
    {
    }

yöntemini CreateModel.OnPostAsync şu şekilde güncelleştirin:

  • Kullanıcı kimliğini modele Contact ekleyin.
  • Kullanıcının kişi oluşturma izni olduğunu doğrulamak için yetkilendirme işleyicisini çağırma.
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 new ChallengeResult();
    }

    Context.Contact.Add(Contact);
    await Context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

IndexModel'i güncelleştirme

Yöntemi yalnızca OnGetAsync onaylanan kişiler genel kullanıcılara gösterilecek şekilde güncelleştirin:

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();
    }
}

EditModel'i güncelleştirme

Kişinin kullanıcıya ait olduğunu doğrulamak için bir yetkilendirme işleyicisi ekleyin. Kaynak yetkilendirmesi doğrulanmış [Authorize] olduğundan, öznitelik yeterli değildir. Öznitelikler değerlendirilirken uygulamanın kaynağa erişimi yok. Kaynak tabanlı yetkilendirme bir gerekliliktir. Denetimler, uygulamanın kaynağa erişimi olduktan sonra, sayfa modeline yükleniyor veya işleyicinin kendi içinde yükleniyor. Kaynak anahtarını geçerek kaynağa sık sık erişebilirsiniz.

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 new ChallengeResult();
        }

        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 new ChallengeResult();
        }

        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");
    }

    private bool ContactExists(int id)
    {
        return Context.Contact.Any(e => e.ContactId == id);
    }
}

DeleteModel'i güncelleştirme

Kullanıcının kişi üzerinde silme izni olduğunu doğrulamak için yetkilendirme işleyicisini kullanmak üzere sayfa silme modelini güncelleştirin.

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 new ChallengeResult();
        }

        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int id)
    {
        Contact = await Context.Contact.FindAsync(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 new ChallengeResult();
        }

        Context.Contact.Remove(Contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Görünümlere yetkilendirme hizmetini ekleme

Kullanıcı arabirimi şu anda kullanıcının değiştirileyemleri için düzenleme ve silme bağlantılarını gösterir.

Yetkilendirme hizmetini tüm görünümler için kullanılabilir _ViewImports.cshtml dosyasına ekleme:

@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

Yukarıdaki işaretleme çeşitli using deyimler ekler.

Sayfalar/Kişiler/Index.cshtml'deki Düzenle ve Sil bağlantılarını yalnızca uygun izinlere sahip kullanıcılar için işlenecek şekilde güncelleştirin:

@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>

Uyarı

Verileri değiştirme izni olmayan kullanıcıların bağlantılarını gizleme, uygulamanın güvenliğini sağlamaz. Bağlantıların gizlenerek yalnızca geçerli bağlantılar görüntüleyerek uygulama daha kolay bir hale geldi. Kullanıcılar sahip olmadığınız veriler üzerinde düzenleme ve silme işlemlerini çağırmak için oluşturulan URL'leri eleilebilir. Sayfa Razor veya denetleyici, verilerin güvenliğini sağlamak için erişim denetimlerini zorunlu hale uygulamalı.

Güncelleştirme Ayrıntıları

Yöneticilerin kişileri onaylaması veya reddetmesi için ayrıntılar görünümünü güncelleştirin:

        @*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-success">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>

Ayrıntılar sayfası modelini güncelleştirin:

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 new ChallengeResult();
        }

        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 new ChallengeResult();
        }
        contact.Status = status;
        Context.Contact.Update(contact);
        await Context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

Role kullanıcı ekleme veya rol kaldırma

Aşağıdakiler hakkında bilgi için bu soruna bakın:

  • Bir kullanıcıdan ayrıcalıkları kaldırma. Örneğin, bir sohbet uygulamasındaki bir kullanıcının adını başka bir kullanıcıya vermek.
  • Kullanıcıya ayrıcalık ekleme.

Tamamlanan uygulamayı test edin

Çekirdek kullanıcı hesapları için henüz parola ayarlamadıysanız, parola ayarlamak için Gizli Dizi Yöneticisi aracını kullanın:

  • Güçlü bir parola seçin: Sekiz veya daha fazla karakter ve en az bir büyük harf karakter, sayı ve sembol kullanın. Örneğin, Passw0rd! güçlü parola gereksinimlerini karşılar.

  • Projenin klasöründen aşağıdaki komutu yürütün; burada <PW> paroladır:

    dotnet user-secrets set SeedUserPW <PW>
    
  • Veritabanını bırakma ve güncelleştirme

    dotnet ef database drop -f
    dotnet ef database update  
    
  • Veritabanının çekirdeğini oluşturmak için uygulamayı yeniden başlatın.

Tamamlanan uygulamayı test etmek için kolay bir yol, üç farklı tarayıcı (veya gizli/InPrivate oturumları) başlatmaktır. Bir tarayıcıda yeni bir kullanıcı (örneğin, ) test@example.com kaydettirin. Farklı bir kullanıcıyla her tarayıcıda oturum açın. Aşağıdaki işlemleri doğrulayın:

  • Kayıtlı kullanıcılar, onaylanan tüm iletişim verilerini görüntülemeye devam ediyor.
  • Kayıtlı kullanıcılar kendi verilerini düzenleyebilir/silebilir.
  • Yöneticiler kişi verilerini onaylar/reddeder. DetailsGörünümde Onayla ve Reddet düğmeleri bulunur.
  • Yöneticiler tüm verileri onaylar/reddeder ve düzenleyebilir/silebilir.
Kullanıcı Uygulamanın çekirdeğini Seçenekler
test@example.com Hayır Kendi verilerini düzenleyin/silin.
manager@contoso.com Yes Kendi verilerini onaylama/reddetme ve düzenleme/silme.
admin@contoso.com Yes Tüm verileri onaylayın/reddedin ve düzenleyin/silin.

Yöneticinin tarayıcısında bir kişi oluşturun. Yönetici ilgili kişisinin silme ve düzenleme URL'sini kopyalayın. Test kullanıcılarının bu işlemleri gerçekleştire çalıştığını doğrulamak için bu bağlantıları test kullanıcılarının tarayıcısına yapıştırın.

Başlangıç uygulamasını oluşturma

  • Razor"ContactManager" adlı bir Sayfalar uygulaması oluşturma

    • Uygulamayı Bireysel Kullanıcı Hesapları ile oluşturun.
    • Ad alanının örnekte kullanılan ad alanıyla eşleşmesi için buna "ContactManager" adını girin.
    • -uld SQLite yerine LocalDB belirtir
    dotnet new webapp -o ContactManager -au Individual -uld
    
  • Models/Contact.cs ekleyin:

    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; }
    }
    
  • Modelin Contact iskelesi.

  • İlk geçişi oluşturun ve veritabanını güncelleştirin:

    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
    
  • Pages/_Layout.cshtml dosyasında ContactManager yer noktasını güncelleştirin:

    <a asp-page="/Contacts/Index" class="navbar-brand">ContactManager</a>
    
  • Kişi oluşturarak, düzenleyerek ve silerek uygulamayı test edin

Veritabanının çekirdeğini oluşturma

SeedData sınıfını Data klasörüne ekleyin.

SeedData.Initialize Main çağrısından:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(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 IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Uygulamanın veritabanının çekirdeğini sınayın. İlgili db'de satır varsa seed yöntemi çalıştırnmaz.

Ek kaynaklar